claude-mpm 3.4.10__py3-none-any.whl ā 5.4.55__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/BUILD_NUMBER +1 -0
- claude_mpm/VERSION +1 -0
- claude_mpm/__init__.py +50 -12
- claude_mpm/__main__.py +7 -2
- claude_mpm/agents/BASE_AGENT.md +164 -0
- claude_mpm/agents/BASE_ENGINEER.md +658 -0
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +290 -0
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
- claude_mpm/agents/MEMORY.md +72 -0
- claude_mpm/agents/PM_INSTRUCTIONS.md +1402 -0
- claude_mpm/agents/WORKFLOW.md +111 -0
- claude_mpm/agents/__init__.py +92 -80
- claude_mpm/agents/agent-template.yaml +83 -0
- claude_mpm/agents/agent_loader.py +560 -745
- claude_mpm/agents/agent_loader_integration.py +53 -55
- claude_mpm/agents/agents_metadata.py +186 -27
- claude_mpm/agents/async_agent_loader.py +436 -0
- claude_mpm/agents/base_agent.json +8 -4
- claude_mpm/agents/frontmatter_validator.py +754 -0
- claude_mpm/agents/system_agent_config.py +222 -155
- claude_mpm/agents/templates/README.md +465 -0
- claude_mpm/agents/templates/__init__.py +17 -13
- claude_mpm/agents/templates/circuit-breakers.md +1391 -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 +90 -128
- claude_mpm/cli/__main__.py +33 -0
- claude_mpm/cli/chrome_devtools_installer.py +175 -0
- claude_mpm/cli/commands/__init__.py +36 -12
- claude_mpm/cli/commands/agent_manager.py +1403 -0
- claude_mpm/cli/commands/agent_source.py +774 -0
- claude_mpm/cli/commands/agent_state_manager.py +335 -0
- claude_mpm/cli/commands/agents.py +2503 -168
- claude_mpm/cli/commands/agents_cleanup.py +210 -0
- claude_mpm/cli/commands/agents_discover.py +338 -0
- claude_mpm/cli/commands/aggregate.py +540 -0
- claude_mpm/cli/commands/analyze.py +553 -0
- claude_mpm/cli/commands/analyze_code.py +528 -0
- claude_mpm/cli/commands/auto_configure.py +1053 -0
- claude_mpm/cli/commands/cleanup.py +588 -0
- claude_mpm/cli/commands/cleanup_orphaned_agents.py +150 -0
- claude_mpm/cli/commands/config.py +586 -0
- claude_mpm/cli/commands/configure.py +2654 -0
- claude_mpm/cli/commands/configure_agent_display.py +282 -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 +286 -0
- claude_mpm/cli/commands/debug.py +1386 -0
- claude_mpm/cli/commands/doctor.py +243 -0
- claude_mpm/cli/commands/hook_errors.py +277 -0
- claude_mpm/cli/commands/info.py +195 -74
- claude_mpm/cli/commands/local_deploy.py +534 -0
- claude_mpm/cli/commands/mcp.py +205 -0
- claude_mpm/cli/commands/mcp_command_router.py +161 -0
- claude_mpm/cli/commands/mcp_config.py +154 -0
- claude_mpm/cli/commands/mcp_config_commands.py +20 -0
- claude_mpm/cli/commands/mcp_external_commands.py +249 -0
- claude_mpm/cli/commands/mcp_install_commands.py +346 -0
- claude_mpm/cli/commands/mcp_pipx_config.py +208 -0
- claude_mpm/cli/commands/mcp_server_commands.py +155 -0
- claude_mpm/cli/commands/mcp_setup_external.py +868 -0
- claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
- claude_mpm/cli/commands/memory.py +585 -846
- claude_mpm/cli/commands/monitor.py +228 -310
- claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
- claude_mpm/cli/commands/mpm_init/core.py +759 -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/knowledge_extractor.py +481 -0
- claude_mpm/cli/commands/mpm_init/modes.py +397 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +722 -0
- claude_mpm/cli/commands/mpm_init_cli.py +396 -0
- claude_mpm/cli/commands/mpm_init_handler.py +195 -0
- claude_mpm/cli/commands/postmortem.py +401 -0
- claude_mpm/cli/commands/profile.py +276 -0
- claude_mpm/cli/commands/run.py +910 -488
- claude_mpm/cli/commands/search.py +458 -0
- claude_mpm/cli/commands/skill_source.py +694 -0
- claude_mpm/cli/commands/skills.py +1246 -0
- claude_mpm/cli/commands/summarize.py +413 -0
- claude_mpm/cli/commands/tickets.py +536 -53
- 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 +297 -0
- claude_mpm/cli/helpers.py +105 -0
- claude_mpm/cli/interactive/__init__.py +21 -0
- claude_mpm/cli/interactive/agent_wizard.py +1947 -0
- claude_mpm/cli/interactive/skills_wizard.py +491 -0
- claude_mpm/cli/parser.py +87 -563
- claude_mpm/cli/parsers/__init__.py +35 -0
- claude_mpm/cli/parsers/agent_manager_parser.py +393 -0
- claude_mpm/cli/parsers/agent_source_parser.py +171 -0
- claude_mpm/cli/parsers/agents_parser.py +575 -0
- claude_mpm/cli/parsers/analyze_code_parser.py +170 -0
- claude_mpm/cli/parsers/analyze_parser.py +135 -0
- claude_mpm/cli/parsers/auto_configure_parser.py +120 -0
- claude_mpm/cli/parsers/base_parser.py +644 -0
- claude_mpm/cli/parsers/config_parser.py +208 -0
- claude_mpm/cli/parsers/configure_parser.py +138 -0
- claude_mpm/cli/parsers/dashboard_parser.py +113 -0
- claude_mpm/cli/parsers/debug_parser.py +319 -0
- claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
- claude_mpm/cli/parsers/mcp_parser.py +195 -0
- claude_mpm/cli/parsers/memory_parser.py +138 -0
- claude_mpm/cli/parsers/monitor_parser.py +142 -0
- claude_mpm/cli/parsers/mpm_init_parser.py +311 -0
- claude_mpm/cli/parsers/profile_parser.py +147 -0
- claude_mpm/cli/parsers/run_parser.py +157 -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 +277 -0
- claude_mpm/cli/parsers/source_parser.py +138 -0
- claude_mpm/cli/parsers/tickets_parser.py +203 -0
- claude_mpm/cli/shared/__init__.py +40 -0
- claude_mpm/cli/shared/argument_patterns.py +205 -0
- claude_mpm/cli/shared/base_command.py +242 -0
- claude_mpm/cli/shared/error_handling.py +242 -0
- claude_mpm/cli/shared/output_formatters.py +241 -0
- claude_mpm/cli/startup.py +1743 -0
- claude_mpm/cli/startup_display.py +480 -0
- claude_mpm/cli/startup_logging.py +839 -0
- claude_mpm/cli/utils.py +136 -47
- claude_mpm/cli_module/__init__.py +6 -6
- claude_mpm/cli_module/args.py +188 -140
- claude_mpm/cli_module/commands.py +79 -70
- claude_mpm/cli_module/migration_example.py +42 -64
- claude_mpm/commands/__init__.py +14 -0
- claude_mpm/commands/mpm-config.md +28 -0
- claude_mpm/commands/mpm-doctor.md +20 -0
- claude_mpm/commands/mpm-help.md +20 -0
- claude_mpm/commands/mpm-init.md +120 -0
- claude_mpm/commands/mpm-monitor.md +31 -0
- claude_mpm/commands/mpm-organize.md +120 -0
- claude_mpm/commands/mpm-postmortem.md +21 -0
- claude_mpm/commands/mpm-session-resume.md +30 -0
- claude_mpm/commands/mpm-status.md +20 -0
- claude_mpm/commands/mpm-ticket-view.md +109 -0
- claude_mpm/commands/mpm-version.md +20 -0
- claude_mpm/commands/mpm.md +31 -0
- claude_mpm/config/__init__.py +42 -2
- claude_mpm/config/agent_config.py +402 -0
- claude_mpm/config/agent_presets.py +488 -0
- claude_mpm/config/agent_sources.py +352 -0
- claude_mpm/config/experimental_features.py +217 -0
- claude_mpm/config/model_config.py +428 -0
- claude_mpm/config/paths.py +258 -0
- claude_mpm/config/skill_presets.py +392 -0
- claude_mpm/config/skill_sources.py +590 -0
- claude_mpm/config/socketio_config.py +125 -83
- claude_mpm/constants.py +132 -22
- claude_mpm/core/__init__.py +62 -36
- claude_mpm/core/agent_name_normalizer.py +71 -73
- claude_mpm/core/agent_registry.py +385 -492
- claude_mpm/core/agent_session_manager.py +81 -70
- claude_mpm/core/api_validator.py +330 -0
- claude_mpm/core/base_service.py +159 -122
- claude_mpm/core/cache.py +560 -0
- claude_mpm/core/claude_runner.py +696 -916
- claude_mpm/core/config.py +613 -122
- claude_mpm/core/config_aliases.py +74 -73
- claude_mpm/core/config_constants.py +314 -0
- claude_mpm/core/constants.py +361 -0
- claude_mpm/core/container.py +646 -104
- claude_mpm/core/enums.py +452 -0
- claude_mpm/core/error_handler.py +623 -0
- claude_mpm/core/exceptions.py +536 -0
- claude_mpm/core/factories.py +105 -109
- 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 +278 -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 +213 -0
- claude_mpm/core/framework/loaders/file_loader.py +176 -0
- claude_mpm/core/framework/loaders/instruction_loader.py +222 -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 +485 -414
- claude_mpm/core/hook_error_memory.py +381 -0
- claude_mpm/core/hook_manager.py +246 -86
- claude_mpm/core/hook_performance_config.py +147 -0
- claude_mpm/core/injectable_service.py +72 -63
- claude_mpm/core/instruction_reinforcement_hook.py +267 -0
- claude_mpm/core/interactive_session.py +670 -0
- claude_mpm/core/interfaces.py +570 -164
- claude_mpm/core/lazy.py +467 -0
- claude_mpm/core/log_manager.py +707 -0
- claude_mpm/core/logger.py +295 -134
- claude_mpm/core/logging_config.py +474 -0
- claude_mpm/core/logging_utils.py +520 -0
- claude_mpm/core/minimal_framework_loader.py +24 -22
- claude_mpm/core/mixins.py +30 -29
- claude_mpm/core/oneshot_session.py +594 -0
- claude_mpm/core/optimized_agent_loader.py +479 -0
- claude_mpm/core/optimized_startup.py +554 -0
- claude_mpm/core/output_style_manager.py +483 -0
- claude_mpm/core/pm_hook_interceptor.py +197 -82
- 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 +153 -116
- claude_mpm/core/session_manager.py +179 -64
- claude_mpm/core/shared/__init__.py +17 -0
- claude_mpm/core/shared/config_loader.py +326 -0
- claude_mpm/core/shared/path_resolver.py +281 -0
- claude_mpm/core/shared/singleton_manager.py +221 -0
- claude_mpm/core/socketio_pool.py +400 -137
- claude_mpm/core/system_context.py +38 -0
- claude_mpm/core/tool_access_control.py +64 -57
- claude_mpm/core/types.py +307 -0
- claude_mpm/core/typing_utils.py +553 -0
- claude_mpm/core/unified_agent_registry.py +969 -0
- claude_mpm/core/unified_config.py +570 -0
- claude_mpm/core/unified_paths.py +941 -0
- claude_mpm/dashboard/__init__.py +12 -0
- claude_mpm/dashboard/api/simple_directory.py +261 -0
- claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B0uc0UOD.js +36 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7RN905-.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7xVLGWV.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BIF9m_hv.js +61 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BPYeabCQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BSNlmTZj.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Be7GpZd6.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bh0LDWpI.js +145 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BofRWZRR.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BovzEFCE.js +30 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C30mlcqg.js +165 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4B-KCzX.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4JcI4KD.js +122 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CBBdVcY8.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CDuw-vjf.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C_Usid8X.js +15 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cfqx1Qun.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CiIAseT4.js +128 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CnA0NrzZ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cs_tUR18.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CyWMqx4W.js +43 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzZX-COe.js +220 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzeYkLYB.js +65 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D3k0OPJN.js +4 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9lljYKQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DGkLK5U1.js +267 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DI7hHRFL.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DLVjFsZ3.js +139 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUrLdbGD.js +89 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DY1XQ8fi.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DZX00Y4g.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DaimHw_p.js +68 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +323 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dhb8PKl3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dle-35c7.js +64 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DmxopI1J.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DwBR2MJi.js +60 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/GYwsonyD.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/NqQ1dWOy.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/RJiighC3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Vzk33B_K.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ZGh7QtNv.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bT1r9zLR.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bTOqqlTd.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/eNVUfhuA.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/iEWssX7S.js +162 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/sQeU3Y1z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uuIeMWc-.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.D6-I5TpK.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.m1gL8KXf.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.CgNOuw-d.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
- claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
- claude_mpm/dashboard/static/svelte-build/index.html +36 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
- claude_mpm/experimental/__init__.py +10 -0
- claude_mpm/experimental/cli_enhancements.py +104 -89
- claude_mpm/generators/__init__.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +76 -66
- claude_mpm/hooks/__init__.py +37 -1
- claude_mpm/hooks/base_hook.py +37 -32
- claude_mpm/hooks/claude_hooks/__init__.py +1 -1
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/connection_pool.py +250 -0
- claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +888 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +652 -875
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +10 -7
- claude_mpm/hooks/claude_hooks/installer.py +806 -0
- claude_mpm/hooks/claude_hooks/memory_integration.py +249 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +412 -0
- claude_mpm/hooks/claude_hooks/services/__init__.py +15 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +229 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +254 -0
- claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +284 -0
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +224 -0
- 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 +201 -107
- 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 +92 -76
- claude_mpm/hooks/validation_hooks.py +62 -54
- claude_mpm/init.py +518 -83
- claude_mpm/models/__init__.py +9 -9
- claude_mpm/models/agent_definition.py +40 -23
- claude_mpm/models/agent_session.py +538 -0
- claude_mpm/models/git_repository.py +198 -0
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/schemas/__init__.py +12 -0
- claude_mpm/scripts/__init__.py +15 -0
- claude_mpm/scripts/claude-hook-handler.sh +227 -0
- claude_mpm/scripts/launch_monitor.py +165 -0
- claude_mpm/scripts/mpm_doctor.py +322 -0
- claude_mpm/scripts/socketio_daemon.py +189 -200
- claude_mpm/scripts/start_activity_logging.py +91 -0
- claude_mpm/services/__init__.py +208 -39
- claude_mpm/services/agent_capabilities_service.py +266 -0
- claude_mpm/services/agents/__init__.py +89 -0
- claude_mpm/services/agents/agent_builder.py +514 -0
- claude_mpm/services/agents/agent_preset_service.py +238 -0
- claude_mpm/services/agents/agent_recommendation_service.py +278 -0
- claude_mpm/services/agents/agent_review_service.py +280 -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/__init__.py +21 -0
- claude_mpm/services/agents/deployment/agent_config_provider.py +410 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +358 -0
- claude_mpm/services/agents/deployment/agent_definition_factory.py +80 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +1037 -0
- claude_mpm/services/agents/deployment/agent_discovery_service.py +546 -0
- claude_mpm/services/agents/deployment/agent_environment_manager.py +288 -0
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +383 -0
- claude_mpm/services/agents/deployment/agent_format_converter.py +505 -0
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +160 -0
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +957 -0
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +273 -0
- claude_mpm/services/agents/deployment/agent_operation_service.py +573 -0
- claude_mpm/services/agents/deployment/agent_record_service.py +418 -0
- claude_mpm/services/agents/deployment/agent_restore_handler.py +84 -0
- claude_mpm/services/agents/deployment/agent_state_service.py +381 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +1369 -0
- claude_mpm/services/agents/deployment/agent_validator.py +376 -0
- claude_mpm/services/agents/deployment/agent_version_manager.py +322 -0
- claude_mpm/services/{agent_versioning.py ā agents/deployment/agent_versioning.py} +10 -13
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +149 -0
- claude_mpm/services/agents/deployment/async_agent_deployment.py +768 -0
- claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
- claude_mpm/services/agents/deployment/config/__init__.py +13 -0
- claude_mpm/services/agents/deployment/config/deployment_config.py +181 -0
- claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
- claude_mpm/services/agents/deployment/deployment_config_loader.py +178 -0
- claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +120 -0
- claude_mpm/services/agents/deployment/deployment_wrapper.py +129 -0
- claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
- claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +70 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +269 -0
- claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
- claude_mpm/services/agents/deployment/interface_adapter.py +226 -0
- claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
- claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
- claude_mpm/services/agents/deployment/local_template_deployment.py +362 -0
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +1478 -0
- claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +162 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +240 -0
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +110 -0
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +80 -0
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +92 -0
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +101 -0
- claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +102 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +269 -0
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +311 -0
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +862 -0
- claude_mpm/services/agents/deployment/results/__init__.py +13 -0
- claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
- claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +113 -0
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +148 -0
- claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +131 -0
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +130 -0
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +228 -0
- claude_mpm/services/agents/deployment/validation/__init__.py +21 -0
- claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
- claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +319 -0
- claude_mpm/services/agents/deployment/validation/validation_result.py +214 -0
- claude_mpm/services/agents/git_source_manager.py +682 -0
- claude_mpm/services/agents/loading/__init__.py +11 -0
- claude_mpm/services/{agent_profile_loader.py ā agents/loading/agent_profile_loader.py} +306 -228
- claude_mpm/services/{base_agent_manager.py ā agents/loading/base_agent_manager.py} +106 -91
- claude_mpm/services/agents/loading/framework_agent_loader.py +433 -0
- claude_mpm/services/agents/local_template_manager.py +784 -0
- claude_mpm/services/agents/management/__init__.py +9 -0
- claude_mpm/services/{agent_capabilities_generator.py ā agents/management/agent_capabilities_generator.py} +92 -69
- claude_mpm/services/{agent_management_service.py ā agents/management/agent_management_service.py} +219 -168
- claude_mpm/services/agents/memory/__init__.py +22 -0
- claude_mpm/services/agents/memory/agent_memory_manager.py +784 -0
- claude_mpm/services/{agent_persistence_service.py ā agents/memory/agent_persistence_service.py} +20 -18
- claude_mpm/services/agents/memory/content_manager.py +470 -0
- claude_mpm/services/agents/memory/memory_categorization_service.py +167 -0
- claude_mpm/services/agents/memory/memory_file_service.py +129 -0
- claude_mpm/services/agents/memory/memory_format_service.py +201 -0
- claude_mpm/services/agents/memory/memory_limits_service.py +101 -0
- claude_mpm/services/agents/memory/template_generator.py +83 -0
- claude_mpm/services/agents/observers.py +547 -0
- claude_mpm/services/agents/recommender.py +617 -0
- claude_mpm/services/agents/registry/__init__.py +30 -0
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +273 -0
- claude_mpm/services/{agent_modification_tracker.py ā agents/registry/modification_tracker.py} +370 -295
- 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 +1202 -0
- claude_mpm/services/agents/startup_sync.py +259 -0
- claude_mpm/services/agents/toolchain_detector.py +478 -0
- claude_mpm/services/analysis/__init__.py +35 -0
- claude_mpm/services/analysis/clone_detector.py +1030 -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 +665 -0
- claude_mpm/services/claude_session_logger.py +321 -0
- claude_mpm/services/cli/__init__.py +18 -0
- claude_mpm/services/cli/agent_cleanup_service.py +408 -0
- claude_mpm/services/cli/agent_dependency_service.py +395 -0
- claude_mpm/services/cli/agent_listing_service.py +463 -0
- claude_mpm/services/cli/agent_output_formatter.py +605 -0
- claude_mpm/services/cli/agent_validation_service.py +590 -0
- claude_mpm/services/cli/memory_crud_service.py +622 -0
- claude_mpm/services/cli/memory_output_formatter.py +604 -0
- claude_mpm/services/cli/resume_service.py +617 -0
- claude_mpm/services/cli/session_manager.py +604 -0
- 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 +362 -0
- claude_mpm/services/cli/unified_dashboard_manager.py +439 -0
- claude_mpm/services/command_deployment_service.py +446 -0
- claude_mpm/services/command_handler_service.py +221 -0
- claude_mpm/services/communication/__init__.py +22 -0
- claude_mpm/services/core/__init__.py +108 -0
- claude_mpm/services/core/base.py +269 -0
- claude_mpm/services/core/cache_manager.py +309 -0
- claude_mpm/services/core/interfaces/__init__.py +273 -0
- claude_mpm/services/core/interfaces/agent.py +514 -0
- claude_mpm/services/core/interfaces/communication.py +316 -0
- claude_mpm/services/core/interfaces/health.py +169 -0
- claude_mpm/services/core/interfaces/infrastructure.py +357 -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/service.py +405 -0
- claude_mpm/services/core/interfaces/stability.py +260 -0
- claude_mpm/services/core/interfaces.py +81 -0
- claude_mpm/services/core/memory_manager.py +682 -0
- 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 +517 -0
- claude_mpm/services/core/service_container.py +520 -0
- claude_mpm/services/core/service_interfaces.py +436 -0
- claude_mpm/services/diagnostics/__init__.py +18 -0
- claude_mpm/services/diagnostics/checks/__init__.py +38 -0
- claude_mpm/services/diagnostics/checks/agent_check.py +370 -0
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +577 -0
- claude_mpm/services/diagnostics/checks/base_check.py +60 -0
- claude_mpm/services/diagnostics/checks/claude_code_check.py +270 -0
- claude_mpm/services/diagnostics/checks/common_issues_check.py +363 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +306 -0
- claude_mpm/services/diagnostics/checks/filesystem_check.py +233 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +520 -0
- claude_mpm/services/diagnostics/checks/instructions_check.py +415 -0
- claude_mpm/services/diagnostics/checks/mcp_check.py +330 -0
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +1058 -0
- claude_mpm/services/diagnostics/checks/monitor_check.py +281 -0
- claude_mpm/services/diagnostics/checks/skill_sources_check.py +587 -0
- claude_mpm/services/diagnostics/checks/startup_log_check.py +319 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +286 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +578 -0
- claude_mpm/services/diagnostics/models.py +138 -0
- claude_mpm/services/event_aggregator.py +582 -0
- claude_mpm/services/event_bus/__init__.py +18 -0
- claude_mpm/services/event_bus/config.py +186 -0
- claude_mpm/services/event_bus/direct_relay.py +312 -0
- claude_mpm/services/event_bus/event_bus.py +396 -0
- claude_mpm/services/event_bus/relay.py +326 -0
- claude_mpm/services/events/__init__.py +44 -0
- claude_mpm/services/events/consumers/__init__.py +18 -0
- claude_mpm/services/events/consumers/dead_letter.py +306 -0
- claude_mpm/services/events/consumers/logging.py +184 -0
- claude_mpm/services/events/consumers/metrics.py +241 -0
- claude_mpm/services/events/consumers/socketio.py +377 -0
- claude_mpm/services/events/core.py +480 -0
- claude_mpm/services/events/interfaces.py +214 -0
- claude_mpm/services/events/producers/__init__.py +14 -0
- claude_mpm/services/events/producers/hook.py +269 -0
- claude_mpm/services/events/producers/system.py +329 -0
- claude_mpm/services/exceptions.py +433 -353
- claude_mpm/services/framework_claude_md_generator/__init__.py +81 -80
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +74 -67
- claude_mpm/services/framework_claude_md_generator/content_validator.py +66 -62
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +82 -60
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +36 -37
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +41 -40
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +15 -15
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +26 -30
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
- claude_mpm/services/framework_claude_md_generator/version_manager.py +31 -30
- claude_mpm/services/git/__init__.py +21 -0
- claude_mpm/services/git/git_operations_service.py +579 -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 +159 -111
- claude_mpm/services/infrastructure/__init__.py +52 -0
- claude_mpm/services/infrastructure/context_preservation.py +569 -0
- claude_mpm/services/infrastructure/daemon_manager.py +279 -0
- claude_mpm/services/infrastructure/logging.py +209 -0
- claude_mpm/services/infrastructure/monitoring/__init__.py +39 -0
- claude_mpm/services/infrastructure/monitoring/aggregator.py +432 -0
- claude_mpm/services/infrastructure/monitoring/base.py +122 -0
- claude_mpm/services/infrastructure/monitoring/legacy.py +203 -0
- claude_mpm/services/infrastructure/monitoring/network.py +219 -0
- claude_mpm/services/infrastructure/monitoring/process.py +343 -0
- claude_mpm/services/infrastructure/monitoring/resources.py +244 -0
- claude_mpm/services/infrastructure/monitoring/service.py +368 -0
- claude_mpm/services/infrastructure/monitoring.py +71 -0
- 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_service_verifier.py +732 -0
- claude_mpm/services/memory/__init__.py +19 -0
- claude_mpm/services/{memory_builder.py ā memory/builder.py} +465 -373
- claude_mpm/services/memory/cache/__init__.py +14 -0
- claude_mpm/services/{shared_prompt_cache.py ā memory/cache/shared_prompt_cache.py} +237 -200
- claude_mpm/services/memory/cache/simple_cache.py +331 -0
- claude_mpm/services/memory/failure_tracker.py +578 -0
- claude_mpm/services/memory/indexed_memory.py +648 -0
- claude_mpm/services/{memory_optimizer.py ā memory/optimizer.py} +272 -243
- claude_mpm/services/memory/router.py +951 -0
- claude_mpm/services/memory_hook_service.py +470 -0
- 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 +698 -0
- claude_mpm/services/monitor/daemon_manager.py +1076 -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 +730 -0
- claude_mpm/services/monitor/server.py +1493 -0
- claude_mpm/services/monitor_build_service.py +349 -0
- claude_mpm/services/native_agent_converter.py +356 -0
- claude_mpm/services/orphan_detection.py +786 -0
- claude_mpm/services/pm_skills_deployer.py +707 -0
- claude_mpm/services/port_manager.py +597 -0
- claude_mpm/services/pr/__init__.py +14 -0
- claude_mpm/services/pr/pr_template_service.py +329 -0
- claude_mpm/services/profile_manager.py +337 -0
- claude_mpm/services/project/__init__.py +44 -0
- claude_mpm/services/{project_analyzer.py ā project/analyzer.py} +541 -291
- claude_mpm/services/project/analyzer_v2.py +566 -0
- claude_mpm/services/project/architecture_analyzer.py +461 -0
- claude_mpm/services/project/archive_manager.py +1045 -0
- claude_mpm/services/project/dependency_analyzer.py +462 -0
- 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/language_analyzer.py +265 -0
- claude_mpm/services/project/metrics_collector.py +407 -0
- claude_mpm/services/project/project_organizer.py +1009 -0
- claude_mpm/services/project/registry.py +636 -0
- claude_mpm/services/project/toolchain_analyzer.py +583 -0
- claude_mpm/services/project_port_allocator.py +596 -0
- claude_mpm/services/recovery_manager.py +293 -240
- claude_mpm/services/response_tracker.py +267 -0
- claude_mpm/services/runner_configuration_service.py +605 -0
- claude_mpm/services/self_upgrade_service.py +608 -0
- claude_mpm/services/session_management_service.py +314 -0
- claude_mpm/services/session_manager.py +380 -0
- claude_mpm/services/shared/__init__.py +21 -0
- claude_mpm/services/shared/async_service_base.py +216 -0
- claude_mpm/services/shared/config_service_base.py +301 -0
- claude_mpm/services/shared/lifecycle_service_base.py +308 -0
- claude_mpm/services/shared/manager_base.py +315 -0
- claude_mpm/services/shared/service_factory.py +309 -0
- claude_mpm/services/skills/__init__.py +21 -0
- claude_mpm/services/skills/git_skill_source_manager.py +1324 -0
- claude_mpm/services/skills/selective_skill_deployer.py +744 -0
- claude_mpm/services/skills/skill_discovery_service.py +568 -0
- claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
- claude_mpm/services/skills_config.py +547 -0
- claude_mpm/services/skills_deployer.py +1168 -0
- claude_mpm/services/socketio/__init__.py +25 -0
- claude_mpm/services/socketio/client_proxy.py +229 -0
- claude_mpm/services/socketio/dashboard_server.py +362 -0
- claude_mpm/services/socketio/event_normalizer.py +798 -0
- claude_mpm/services/socketio/handlers/__init__.py +30 -0
- claude_mpm/services/socketio/handlers/base.py +136 -0
- claude_mpm/services/socketio/handlers/code_analysis.py +682 -0
- claude_mpm/services/socketio/handlers/connection.py +643 -0
- claude_mpm/services/socketio/handlers/connection_handler.py +333 -0
- claude_mpm/services/socketio/handlers/file.py +263 -0
- claude_mpm/services/socketio/handlers/git.py +962 -0
- claude_mpm/services/socketio/handlers/hook.py +211 -0
- claude_mpm/services/socketio/handlers/memory.py +26 -0
- claude_mpm/services/socketio/handlers/project.py +24 -0
- claude_mpm/services/socketio/handlers/registry.py +214 -0
- claude_mpm/services/socketio/migration_utils.py +343 -0
- claude_mpm/services/socketio/monitor_client.py +364 -0
- claude_mpm/services/socketio/server/__init__.py +18 -0
- claude_mpm/services/socketio/server/broadcaster.py +569 -0
- claude_mpm/services/socketio/server/connection_manager.py +579 -0
- claude_mpm/services/socketio/server/core.py +1079 -0
- claude_mpm/services/socketio/server/eventbus_integration.py +245 -0
- claude_mpm/services/socketio/server/main.py +501 -0
- claude_mpm/services/socketio_client_manager.py +173 -143
- claude_mpm/services/socketio_server.py +38 -1657
- claude_mpm/services/subprocess_launcher_service.py +322 -0
- claude_mpm/services/system_instructions_service.py +270 -0
- claude_mpm/services/ticket_manager.py +25 -209
- claude_mpm/services/ticket_services/__init__.py +26 -0
- claude_mpm/services/ticket_services/crud_service.py +328 -0
- claude_mpm/services/ticket_services/formatter_service.py +290 -0
- claude_mpm/services/ticket_services/search_service.py +324 -0
- claude_mpm/services/ticket_services/validation_service.py +303 -0
- claude_mpm/services/ticket_services/workflow_service.py +244 -0
- 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 +280 -0
- claude_mpm/services/version_control/__init__.py +34 -37
- claude_mpm/services/version_control/branch_strategy.py +26 -17
- claude_mpm/services/version_control/conflict_resolution.py +52 -36
- claude_mpm/services/version_control/git_operations.py +183 -49
- claude_mpm/services/version_control/semantic_versioning.py +172 -61
- claude_mpm/services/version_control/version_parser.py +546 -0
- claude_mpm/services/version_service.py +379 -0
- claude_mpm/services/visualization/__init__.py +15 -0
- claude_mpm/services/visualization/mermaid_generator.py +937 -0
- 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 +439 -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 +405 -0
- claude_mpm/skills/skills_registry.py +347 -0
- claude_mpm/skills/skills_service.py +739 -0
- claude_mpm/storage/__init__.py +9 -0
- claude_mpm/storage/state_storage.py +546 -0
- claude_mpm/templates/.pre-commit-config.yaml +112 -0
- 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/ticket_wrapper.py +2 -2
- claude_mpm/tools/__init__.py +10 -0
- claude_mpm/tools/__main__.py +208 -0
- 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 +631 -0
- claude_mpm/tools/code_tree_events.py +420 -0
- claude_mpm/tools/socketio_debug.py +671 -0
- claude_mpm/utils/__init__.py +8 -8
- claude_mpm/utils/agent_dependency_loader.py +1090 -0
- claude_mpm/utils/agent_filters.py +261 -0
- claude_mpm/utils/common.py +544 -0
- claude_mpm/utils/config_manager.py +168 -126
- claude_mpm/utils/console.py +11 -0
- claude_mpm/utils/database_connector.py +298 -0
- claude_mpm/utils/dependency_cache.py +373 -0
- claude_mpm/utils/dependency_manager.py +60 -59
- claude_mpm/utils/dependency_strategies.py +381 -0
- claude_mpm/utils/display_helper.py +260 -0
- claude_mpm/utils/environment_context.py +313 -0
- claude_mpm/utils/error_handler.py +78 -66
- claude_mpm/utils/file_utils.py +305 -0
- claude_mpm/utils/framework_detection.py +12 -11
- claude_mpm/utils/git_analyzer.py +407 -0
- claude_mpm/utils/gitignore.py +244 -0
- claude_mpm/utils/import_migration_example.py +12 -60
- claude_mpm/utils/imports.py +48 -45
- claude_mpm/utils/log_cleanup.py +627 -0
- claude_mpm/utils/migration.py +372 -0
- claude_mpm/utils/path_operations.py +110 -104
- claude_mpm/utils/progress.py +387 -0
- claude_mpm/utils/robust_installer.py +823 -0
- claude_mpm/utils/session_logging.py +121 -0
- claude_mpm/utils/structured_questions.py +619 -0
- claude_mpm/utils/subprocess_utils.py +343 -0
- claude_mpm/validation/__init__.py +1 -1
- claude_mpm/validation/agent_validator.py +214 -108
- claude_mpm/validation/frontmatter_validator.py +252 -0
- claude_mpm-5.4.55.dist-info/METADATA +999 -0
- claude_mpm-5.4.55.dist-info/RECORD +868 -0
- {claude_mpm-3.4.10.dist-info ā claude_mpm-5.4.55.dist-info}/entry_points.txt +1 -3
- claude_mpm-5.4.55.dist-info/licenses/LICENSE +94 -0
- claude_mpm-5.4.55.dist-info/licenses/LICENSE-FAQ.md +153 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -88
- claude_mpm/agents/INSTRUCTIONS.md +0 -352
- claude_mpm/agents/backups/INSTRUCTIONS.md +0 -352
- claude_mpm/agents/base_agent_loader.py +0 -529
- claude_mpm/agents/schema/agent_schema.json +0 -314
- claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -36
- claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json +0 -46
- claude_mpm/agents/templates/backup/documentation_agent_20250726_234551.json +0 -45
- claude_mpm/agents/templates/backup/engineer_agent_20250726_234551.json +0 -49
- claude_mpm/agents/templates/backup/ops_agent_20250726_234551.json +0 -46
- claude_mpm/agents/templates/backup/qa_agent_20250726_234551.json +0 -45
- claude_mpm/agents/templates/backup/research_agent_20250726_234551.json +0 -49
- claude_mpm/agents/templates/backup/security_agent_20250726_234551.json +0 -46
- claude_mpm/agents/templates/backup/version_control_agent_20250726_234551.json +0 -46
- claude_mpm/agents/templates/data_engineer.json +0 -110
- claude_mpm/agents/templates/documentation.json +0 -109
- claude_mpm/agents/templates/engineer.json +0 -113
- claude_mpm/agents/templates/ops.json +0 -109
- claude_mpm/agents/templates/pm.json +0 -25
- claude_mpm/agents/templates/qa.json +0 -111
- claude_mpm/agents/templates/research.json +0 -65
- claude_mpm/agents/templates/security.json +0 -113
- claude_mpm/agents/templates/test_integration.json +0 -112
- claude_mpm/agents/templates/version_control.json +0 -107
- claude_mpm/cli/commands/ui.py +0 -57
- claude_mpm/core/simple_runner.py +0 -1046
- claude_mpm/dashboard/open_dashboard.py +0 -34
- claude_mpm/deployment_paths.py +0 -261
- claude_mpm/hooks/builtin/__init__.py +0 -1
- claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
- claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
- claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
- claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
- claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
- claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
- claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
- claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
- claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
- claude_mpm/orchestration/__init__.py +0 -6
- claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
- claude_mpm/orchestration/archive/factory.py +0 -215
- claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
- claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
- claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
- claude_mpm/orchestration/archive/orchestrator.py +0 -501
- claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
- claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
- claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
- claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
- claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
- claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
- claude_mpm/schemas/workflow_validator.py +0 -411
- claude_mpm/services/agent_deployment.py +0 -1534
- claude_mpm/services/agent_lifecycle_manager.py +0 -1169
- claude_mpm/services/agent_memory_manager.py +0 -1415
- claude_mpm/services/agent_registry.py +0 -676
- claude_mpm/services/deployed_agent_discovery.py +0 -226
- claude_mpm/services/framework_agent_loader.py +0 -337
- claude_mpm/services/framework_claude_md_generator.py +0 -621
- claude_mpm/services/health_monitor.py +0 -892
- claude_mpm/services/memory_router.py +0 -538
- claude_mpm/services/parent_directory_manager/__init__.py +0 -577
- claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
- claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
- claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
- claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
- claude_mpm/services/parent_directory_manager/operations.py +0 -186
- claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
- claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
- claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
- claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
- claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
- claude_mpm/services/standalone_socketio_server.py +0 -1300
- claude_mpm/services/ticket_manager_di.py +0 -318
- claude_mpm/services/ticketing_service_original.py +0 -508
- claude_mpm/ui/__init__.py +0 -1
- claude_mpm/ui/rich_terminal_ui.py +0 -295
- claude_mpm/ui/terminal_ui.py +0 -328
- claude_mpm/utils/paths.py +0 -289
- claude_mpm-3.4.10.dist-info/METADATA +0 -183
- claude_mpm-3.4.10.dist-info/RECORD +0 -201
- claude_mpm-3.4.10.dist-info/licenses/LICENSE +0 -21
- {claude_mpm-3.4.10.dist-info ā claude_mpm-5.4.55.dist-info}/WHEEL +0 -0
- {claude_mpm-3.4.10.dist-info ā claude_mpm-5.4.55.dist-info}/top_level.txt +0 -0
|
@@ -2,190 +2,2525 @@
|
|
|
2
2
|
Agents command implementation for claude-mpm.
|
|
3
3
|
|
|
4
4
|
WHY: This module manages Claude Code native agents, including listing, deploying,
|
|
5
|
-
and cleaning agent deployments.
|
|
5
|
+
and cleaning agent deployments. Refactored to use shared utilities for consistency.
|
|
6
|
+
|
|
7
|
+
DESIGN DECISIONS:
|
|
8
|
+
- Use AgentCommand base class for consistent CLI patterns
|
|
9
|
+
- Leverage shared utilities for argument parsing and output formatting
|
|
10
|
+
- Maintain backward compatibility with existing functionality
|
|
11
|
+
- Support multiple output formats (json, yaml, table, text)
|
|
6
12
|
"""
|
|
7
13
|
|
|
14
|
+
import json
|
|
8
15
|
from pathlib import Path
|
|
9
16
|
|
|
10
|
-
from ...core.logger import get_logger
|
|
11
17
|
from ...constants import AgentCommands
|
|
18
|
+
from ...core.enums import OutputFormat
|
|
19
|
+
from ...services.cli.agent_cleanup_service import AgentCleanupService
|
|
20
|
+
from ...services.cli.agent_dependency_service import AgentDependencyService
|
|
21
|
+
from ...services.cli.agent_listing_service import AgentListingService
|
|
22
|
+
from ...services.cli.agent_output_formatter import AgentOutputFormatter
|
|
23
|
+
from ...services.cli.agent_validation_service import AgentValidationService
|
|
24
|
+
from ..shared import AgentCommand, CommandResult
|
|
12
25
|
from ..utils import get_agent_versions_display
|
|
26
|
+
from .agents_cleanup import handle_agents_cleanup
|
|
13
27
|
|
|
14
28
|
|
|
15
|
-
def
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
def _is_structured_output(args) -> bool:
|
|
30
|
+
"""Check if args specify structured output format (JSON/YAML)."""
|
|
31
|
+
if hasattr(args, "format"):
|
|
32
|
+
fmt = str(args.format).lower()
|
|
33
|
+
return fmt in (OutputFormat.JSON, OutputFormat.YAML)
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AgentsCommand(AgentCommand):
|
|
38
|
+
"""Agent management command using shared utilities."""
|
|
39
|
+
|
|
40
|
+
def __init__(self):
|
|
41
|
+
super().__init__("agents")
|
|
42
|
+
self._deployment_service = None
|
|
43
|
+
self._listing_service = None
|
|
44
|
+
self._validation_service = None
|
|
45
|
+
self._dependency_service = None
|
|
46
|
+
self._cleanup_service = None
|
|
47
|
+
self._formatter = AgentOutputFormatter()
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def deployment_service(self):
|
|
51
|
+
"""Get deployment service instance (lazy loaded)."""
|
|
52
|
+
if self._deployment_service is None:
|
|
53
|
+
try:
|
|
54
|
+
from ...services import AgentDeploymentService
|
|
55
|
+
from ...services.agents.deployment.deployment_wrapper import (
|
|
56
|
+
DeploymentServiceWrapper,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
base_service = AgentDeploymentService()
|
|
60
|
+
self._deployment_service = DeploymentServiceWrapper(base_service)
|
|
61
|
+
except ImportError as e:
|
|
62
|
+
raise ImportError("Agent deployment service not available") from e
|
|
63
|
+
return self._deployment_service
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def listing_service(self):
|
|
67
|
+
"""Get listing service instance (lazy loaded)."""
|
|
68
|
+
if self._listing_service is None:
|
|
69
|
+
self._listing_service = AgentListingService(
|
|
70
|
+
deployment_service=self.deployment_service
|
|
71
|
+
)
|
|
72
|
+
return self._listing_service
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def validation_service(self):
|
|
76
|
+
"""Get validation service instance (lazy loaded)."""
|
|
77
|
+
if self._validation_service is None:
|
|
78
|
+
self._validation_service = AgentValidationService()
|
|
79
|
+
return self._validation_service
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def dependency_service(self):
|
|
83
|
+
"""Get dependency service instance (lazy loaded)."""
|
|
84
|
+
if self._dependency_service is None:
|
|
85
|
+
self._dependency_service = AgentDependencyService()
|
|
86
|
+
return self._dependency_service
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def cleanup_service(self):
|
|
90
|
+
"""Get cleanup service instance (lazy loaded)."""
|
|
91
|
+
if self._cleanup_service is None:
|
|
92
|
+
self._cleanup_service = AgentCleanupService(
|
|
93
|
+
deployment_service=self.deployment_service
|
|
94
|
+
)
|
|
95
|
+
return self._cleanup_service
|
|
96
|
+
|
|
97
|
+
def _get_output_format(self, args) -> str:
|
|
98
|
+
"""
|
|
99
|
+
Get output format from args with enum default.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
args: Command arguments
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Output format string (compatible with both enum and string usage)
|
|
106
|
+
"""
|
|
107
|
+
return getattr(args, "format", OutputFormat.TEXT)
|
|
108
|
+
|
|
109
|
+
def _is_structured_format(self, format_str: str) -> bool:
|
|
110
|
+
"""
|
|
111
|
+
Check if format is structured (JSON/YAML).
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
format_str: Format string to check
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
True if format is JSON or YAML
|
|
118
|
+
"""
|
|
119
|
+
fmt = str(format_str).lower()
|
|
120
|
+
return fmt in (OutputFormat.JSON, OutputFormat.YAML)
|
|
121
|
+
|
|
122
|
+
def validate_args(self, args) -> str:
|
|
123
|
+
"""Validate command arguments."""
|
|
124
|
+
# Most agent commands are optional, so basic validation
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
def run(self, args) -> CommandResult:
|
|
128
|
+
"""Execute the agent command."""
|
|
129
|
+
try:
|
|
130
|
+
# Handle default case (no subcommand)
|
|
131
|
+
if not hasattr(args, "agents_command") or not args.agents_command:
|
|
132
|
+
return self._show_agent_versions(args)
|
|
133
|
+
|
|
134
|
+
# Route to appropriate subcommand
|
|
135
|
+
command_map = {
|
|
136
|
+
AgentCommands.LIST.value: self._list_agents,
|
|
137
|
+
AgentCommands.DEPLOY.value: lambda a: self._deploy_agents(
|
|
138
|
+
a, force=False
|
|
139
|
+
),
|
|
140
|
+
AgentCommands.FORCE_DEPLOY.value: lambda a: self._deploy_agents(
|
|
141
|
+
a, force=True
|
|
142
|
+
),
|
|
143
|
+
AgentCommands.CLEAN.value: self._clean_agents,
|
|
144
|
+
AgentCommands.VIEW.value: self._view_agent,
|
|
145
|
+
AgentCommands.FIX.value: self._fix_agents,
|
|
146
|
+
"deps-check": self._check_agent_dependencies,
|
|
147
|
+
"deps-install": self._install_agent_dependencies,
|
|
148
|
+
"deps-list": self._list_agent_dependencies,
|
|
149
|
+
"deps-fix": self._fix_agent_dependencies,
|
|
150
|
+
"cleanup": lambda a: self._handle_cleanup_command(a),
|
|
151
|
+
"cleanup-orphaned": self._cleanup_orphaned_agents,
|
|
152
|
+
# Local agent management commands
|
|
153
|
+
"create": self._create_local_agent,
|
|
154
|
+
"edit": self._edit_local_agent,
|
|
155
|
+
"delete": self._delete_local_agent,
|
|
156
|
+
"manage": self._manage_local_agents,
|
|
157
|
+
"configure": self._configure_deployment,
|
|
158
|
+
# Migration command (DEPRECATION support)
|
|
159
|
+
"migrate-to-project": self._migrate_to_project,
|
|
160
|
+
# Agent selection modes (Phase 3: 1M-382)
|
|
161
|
+
"deploy-minimal": self._deploy_minimal_configuration,
|
|
162
|
+
"deploy-auto": self._deploy_auto_configure,
|
|
163
|
+
# Agent source management (Phase 2: 1M-442)
|
|
164
|
+
"available": self._list_available_from_sources,
|
|
165
|
+
# Agent discovery with rich filtering (Phase 1: Discovery & Browsing)
|
|
166
|
+
"discover": self._discover_agents,
|
|
167
|
+
# NEW: Collection-based agent management
|
|
168
|
+
"list-collections": self._list_collections,
|
|
169
|
+
"deploy-collection": self._deploy_collection,
|
|
170
|
+
"list-by-collection": self._list_by_collection,
|
|
171
|
+
# Cache git management commands
|
|
172
|
+
"cache-status": self._cache_status,
|
|
173
|
+
"cache-pull": self._cache_pull,
|
|
174
|
+
"cache-push": self._cache_push,
|
|
175
|
+
"cache-sync": self._cache_sync,
|
|
176
|
+
"cache-commit": self._cache_commit,
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if args.agents_command in command_map:
|
|
180
|
+
return command_map[args.agents_command](args)
|
|
181
|
+
return CommandResult.error_result(
|
|
182
|
+
f"Unknown agent command: {args.agents_command}"
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
except ImportError:
|
|
186
|
+
self.logger.error("Agent deployment service not available")
|
|
187
|
+
return CommandResult.error_result("Agent deployment service not available")
|
|
188
|
+
except Exception as e:
|
|
189
|
+
self.logger.error(f"Error managing agents: {e}", exc_info=True)
|
|
190
|
+
return CommandResult.error_result(f"Error managing agents: {e}")
|
|
191
|
+
|
|
192
|
+
def _show_agent_versions(self, args) -> CommandResult:
|
|
193
|
+
"""Show current agent versions as default action."""
|
|
194
|
+
try:
|
|
38
195
|
agent_versions = get_agent_versions_display()
|
|
196
|
+
|
|
197
|
+
output_format = self._get_output_format(args)
|
|
198
|
+
if self._is_structured_format(output_format):
|
|
199
|
+
# Parse the agent versions display into structured data
|
|
200
|
+
if agent_versions:
|
|
201
|
+
data = {"agent_versions": agent_versions, "has_agents": True}
|
|
202
|
+
formatted = (
|
|
203
|
+
self._formatter.format_as_json(data)
|
|
204
|
+
if str(output_format).lower() == OutputFormat.JSON
|
|
205
|
+
else self._formatter.format_as_yaml(data)
|
|
206
|
+
)
|
|
207
|
+
print(formatted)
|
|
208
|
+
return CommandResult.success_result(
|
|
209
|
+
"Agent versions retrieved", data=data
|
|
210
|
+
)
|
|
211
|
+
data = {
|
|
212
|
+
"agent_versions": None,
|
|
213
|
+
"has_agents": False,
|
|
214
|
+
"suggestion": "To deploy agents, run: claude-mpm --mpm:agents deploy",
|
|
215
|
+
}
|
|
216
|
+
formatted = (
|
|
217
|
+
self._formatter.format_as_json(data)
|
|
218
|
+
if str(output_format).lower() == OutputFormat.JSON
|
|
219
|
+
else self._formatter.format_as_yaml(data)
|
|
220
|
+
)
|
|
221
|
+
print(formatted)
|
|
222
|
+
return CommandResult.success_result(
|
|
223
|
+
"No deployed agents found", data=data
|
|
224
|
+
)
|
|
225
|
+
# Text output
|
|
39
226
|
if agent_versions:
|
|
40
227
|
print(agent_versions)
|
|
41
|
-
|
|
42
|
-
print("No deployed agents found")
|
|
43
|
-
print("\nTo deploy agents, run: claude-mpm --mpm:agents deploy")
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
if args.agents_command == AgentCommands.LIST.value:
|
|
47
|
-
_list_agents(args, deployment_service)
|
|
48
|
-
|
|
49
|
-
elif args.agents_command == AgentCommands.DEPLOY.value:
|
|
50
|
-
_deploy_agents(args, deployment_service, force=False)
|
|
51
|
-
|
|
52
|
-
elif args.agents_command == AgentCommands.FORCE_DEPLOY.value:
|
|
53
|
-
_deploy_agents(args, deployment_service, force=True)
|
|
54
|
-
|
|
55
|
-
elif args.agents_command == AgentCommands.CLEAN.value:
|
|
56
|
-
_clean_agents(args, deployment_service)
|
|
57
|
-
|
|
58
|
-
except ImportError:
|
|
59
|
-
logger.error("Agent deployment service not available")
|
|
60
|
-
print("Error: Agent deployment service not available")
|
|
61
|
-
except Exception as e:
|
|
62
|
-
logger.error(f"Error managing agents: {e}")
|
|
63
|
-
print(f"Error: {e}")
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def _list_agents(args, deployment_service):
|
|
67
|
-
"""
|
|
68
|
-
List available or deployed agents.
|
|
69
|
-
|
|
70
|
-
WHY: Users need to see what agents are available in the system and what's
|
|
71
|
-
currently deployed. This helps them understand the agent ecosystem.
|
|
72
|
-
|
|
73
|
-
Args:
|
|
74
|
-
args: Command arguments with 'system' and 'deployed' flags
|
|
75
|
-
deployment_service: Agent deployment service instance
|
|
76
|
-
"""
|
|
77
|
-
if args.system:
|
|
78
|
-
# List available agent templates
|
|
79
|
-
print("Available Agent Templates:")
|
|
80
|
-
print("-" * 80)
|
|
81
|
-
agents = deployment_service.list_available_agents()
|
|
82
|
-
if not agents:
|
|
83
|
-
print("No agent templates found")
|
|
84
|
-
else:
|
|
85
|
-
for agent in agents:
|
|
86
|
-
print(f"š {agent['file']}")
|
|
87
|
-
if 'name' in agent:
|
|
88
|
-
print(f" Name: {agent['name']}")
|
|
89
|
-
if 'description' in agent:
|
|
90
|
-
print(f" Description: {agent['description']}")
|
|
91
|
-
if 'version' in agent:
|
|
92
|
-
print(f" Version: {agent['version']}")
|
|
93
|
-
print()
|
|
94
|
-
|
|
95
|
-
elif args.deployed:
|
|
96
|
-
# List deployed agents
|
|
97
|
-
print("Deployed Agents:")
|
|
98
|
-
print("-" * 80)
|
|
99
|
-
verification = deployment_service.verify_deployment()
|
|
100
|
-
if not verification["agents_found"]:
|
|
228
|
+
return CommandResult.success_result("Agent versions displayed")
|
|
101
229
|
print("No deployed agents found")
|
|
230
|
+
print("\nTo deploy agents, run: claude-mpm --mpm:agents deploy")
|
|
231
|
+
return CommandResult.success_result("No deployed agents found")
|
|
232
|
+
|
|
233
|
+
except Exception as e:
|
|
234
|
+
self.logger.error(f"Error getting agent versions: {e}", exc_info=True)
|
|
235
|
+
return CommandResult.error_result(f"Error getting agent versions: {e}")
|
|
236
|
+
|
|
237
|
+
def _list_agents(self, args) -> CommandResult:
|
|
238
|
+
"""List available or deployed agents."""
|
|
239
|
+
try:
|
|
240
|
+
output_format = self._get_output_format(args)
|
|
241
|
+
|
|
242
|
+
if hasattr(args, "by_tier") and args.by_tier:
|
|
243
|
+
return self._list_agents_by_tier(args)
|
|
244
|
+
if getattr(args, "system", False):
|
|
245
|
+
return self._list_system_agents(args)
|
|
246
|
+
if getattr(args, "deployed", False):
|
|
247
|
+
return self._list_deployed_agents(args)
|
|
248
|
+
# Default: show usage
|
|
249
|
+
usage_msg = "Use --system to list system agents, --deployed to list deployed agents, or --by-tier to group by precedence"
|
|
250
|
+
|
|
251
|
+
if self._is_structured_format(output_format):
|
|
252
|
+
return CommandResult.error_result(
|
|
253
|
+
"No list option specified",
|
|
254
|
+
data={
|
|
255
|
+
"usage": usage_msg,
|
|
256
|
+
"available_options": ["--system", "--deployed", "--by-tier"],
|
|
257
|
+
},
|
|
258
|
+
)
|
|
259
|
+
print(usage_msg)
|
|
260
|
+
return CommandResult.error_result("No list option specified")
|
|
261
|
+
|
|
262
|
+
except Exception as e:
|
|
263
|
+
self.logger.error(f"Error listing agents: {e}", exc_info=True)
|
|
264
|
+
return CommandResult.error_result(f"Error listing agents: {e}")
|
|
265
|
+
|
|
266
|
+
def _list_system_agents(self, args) -> CommandResult:
|
|
267
|
+
"""List available agent templates."""
|
|
268
|
+
try:
|
|
269
|
+
verbose = getattr(args, "verbose", False)
|
|
270
|
+
agents = self.listing_service.list_system_agents(verbose=verbose)
|
|
271
|
+
|
|
272
|
+
output_format = self._get_output_format(args)
|
|
273
|
+
quiet = getattr(args, "quiet", False)
|
|
274
|
+
|
|
275
|
+
# Convert AgentInfo objects to dicts for formatter
|
|
276
|
+
agents_data = [
|
|
277
|
+
{
|
|
278
|
+
"name": agent.name,
|
|
279
|
+
"type": agent.type,
|
|
280
|
+
"path": agent.path,
|
|
281
|
+
"file": Path(agent.path).name if agent.path else "Unknown",
|
|
282
|
+
"description": agent.description,
|
|
283
|
+
"specializations": agent.specializations,
|
|
284
|
+
"version": agent.version,
|
|
285
|
+
}
|
|
286
|
+
for agent in agents
|
|
287
|
+
]
|
|
288
|
+
|
|
289
|
+
formatted = self._formatter.format_agent_list(
|
|
290
|
+
agents_data, output_format=output_format, verbose=verbose, quiet=quiet
|
|
291
|
+
)
|
|
292
|
+
print(formatted)
|
|
293
|
+
|
|
294
|
+
return CommandResult.success_result(
|
|
295
|
+
f"Listed {len(agents)} agent templates",
|
|
296
|
+
data={"agents": agents_data, "count": len(agents)},
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
except Exception as e:
|
|
300
|
+
self.logger.error(f"Error listing system agents: {e}", exc_info=True)
|
|
301
|
+
return CommandResult.error_result(f"Error listing system agents: {e}")
|
|
302
|
+
|
|
303
|
+
def _list_deployed_agents(self, args) -> CommandResult:
|
|
304
|
+
"""List deployed agents."""
|
|
305
|
+
try:
|
|
306
|
+
verbose = getattr(args, "verbose", False)
|
|
307
|
+
agents, warnings = self.listing_service.list_deployed_agents(
|
|
308
|
+
verbose=verbose
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
output_format = self._get_output_format(args)
|
|
312
|
+
quiet = getattr(args, "quiet", False)
|
|
313
|
+
|
|
314
|
+
# Convert AgentInfo objects to dicts for formatter
|
|
315
|
+
agents_data = [
|
|
316
|
+
{
|
|
317
|
+
"name": agent.name,
|
|
318
|
+
"type": agent.type,
|
|
319
|
+
"tier": agent.tier,
|
|
320
|
+
"path": agent.path,
|
|
321
|
+
"file": Path(agent.path).name if agent.path else "Unknown",
|
|
322
|
+
"description": agent.description,
|
|
323
|
+
"specializations": agent.specializations,
|
|
324
|
+
"version": agent.version,
|
|
325
|
+
}
|
|
326
|
+
for agent in agents
|
|
327
|
+
]
|
|
328
|
+
|
|
329
|
+
# Format the agent list
|
|
330
|
+
formatted = self._formatter.format_agent_list(
|
|
331
|
+
agents_data, output_format=output_format, verbose=verbose, quiet=quiet
|
|
332
|
+
)
|
|
333
|
+
print(formatted)
|
|
334
|
+
|
|
335
|
+
# Add warnings for text output
|
|
336
|
+
if str(output_format).lower() == OutputFormat.TEXT and warnings:
|
|
337
|
+
print("\nWarnings:")
|
|
338
|
+
for warning in warnings:
|
|
339
|
+
print(f" ā ļø {warning}")
|
|
340
|
+
|
|
341
|
+
return CommandResult.success_result(
|
|
342
|
+
f"Listed {len(agents)} deployed agents",
|
|
343
|
+
data={
|
|
344
|
+
"agents": agents_data,
|
|
345
|
+
"warnings": warnings,
|
|
346
|
+
"count": len(agents),
|
|
347
|
+
},
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
except Exception as e:
|
|
351
|
+
self.logger.error(f"Error listing deployed agents: {e}", exc_info=True)
|
|
352
|
+
return CommandResult.error_result(f"Error listing deployed agents: {e}")
|
|
353
|
+
|
|
354
|
+
def _list_agents_by_tier(self, args) -> CommandResult:
|
|
355
|
+
"""List agents grouped by tier/precedence."""
|
|
356
|
+
try:
|
|
357
|
+
tier_info = self.listing_service.list_agents_by_tier()
|
|
358
|
+
output_format = self._get_output_format(args)
|
|
359
|
+
|
|
360
|
+
# Convert to format expected by formatter
|
|
361
|
+
agents_by_tier = {
|
|
362
|
+
"project": [
|
|
363
|
+
{
|
|
364
|
+
"name": agent.name,
|
|
365
|
+
"type": agent.type,
|
|
366
|
+
"path": agent.path,
|
|
367
|
+
"active": agent.active,
|
|
368
|
+
"overridden_by": agent.overridden_by,
|
|
369
|
+
}
|
|
370
|
+
for agent in tier_info.project
|
|
371
|
+
],
|
|
372
|
+
"user": [
|
|
373
|
+
{
|
|
374
|
+
"name": agent.name,
|
|
375
|
+
"type": agent.type,
|
|
376
|
+
"path": agent.path,
|
|
377
|
+
"active": agent.active,
|
|
378
|
+
"overridden_by": agent.overridden_by,
|
|
379
|
+
}
|
|
380
|
+
for agent in tier_info.user
|
|
381
|
+
],
|
|
382
|
+
"system": [
|
|
383
|
+
{
|
|
384
|
+
"name": agent.name,
|
|
385
|
+
"type": agent.type,
|
|
386
|
+
"path": agent.path,
|
|
387
|
+
"active": agent.active,
|
|
388
|
+
"overridden_by": agent.overridden_by,
|
|
389
|
+
}
|
|
390
|
+
for agent in tier_info.system
|
|
391
|
+
],
|
|
392
|
+
"summary": {
|
|
393
|
+
"total_count": tier_info.total_count,
|
|
394
|
+
"active_count": tier_info.active_count,
|
|
395
|
+
"project_count": len(tier_info.project),
|
|
396
|
+
"user_count": len(tier_info.user),
|
|
397
|
+
"system_count": len(tier_info.system),
|
|
398
|
+
},
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
formatted = self._formatter.format_agents_by_tier(
|
|
402
|
+
agents_by_tier, output_format=output_format
|
|
403
|
+
)
|
|
404
|
+
print(formatted)
|
|
405
|
+
|
|
406
|
+
return CommandResult.success_result(
|
|
407
|
+
"Agents listed by tier", data=agents_by_tier
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
except Exception as e:
|
|
411
|
+
self.logger.error(f"Error listing agents by tier: {e}", exc_info=True)
|
|
412
|
+
return CommandResult.error_result(f"Error listing agents by tier: {e}")
|
|
413
|
+
|
|
414
|
+
def _list_available_from_sources(self, args) -> CommandResult:
|
|
415
|
+
"""List available agents from all configured git sources.
|
|
416
|
+
|
|
417
|
+
This command shows agents discovered from configured agent sources
|
|
418
|
+
(Git repositories) after syncing their cache. Implements Phase 2 of 1M-442.
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
args: Command arguments with optional source filter and format
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
CommandResult with agent list or error
|
|
425
|
+
"""
|
|
426
|
+
try:
|
|
427
|
+
from ...config.agent_sources import AgentSourceConfiguration
|
|
428
|
+
from ...services.agents.git_source_manager import GitSourceManager
|
|
429
|
+
|
|
430
|
+
# Load agent sources configuration
|
|
431
|
+
config = AgentSourceConfiguration.load()
|
|
432
|
+
enabled_repos = [r for r in config.repositories if r.enabled]
|
|
433
|
+
|
|
434
|
+
if not enabled_repos:
|
|
435
|
+
message = (
|
|
436
|
+
"No agent sources configured.\n\n"
|
|
437
|
+
"Configure sources with:\n"
|
|
438
|
+
" claude-mpm agent-source add <url>\n\n"
|
|
439
|
+
"Example:\n"
|
|
440
|
+
" claude-mpm agent-source add https://github.com/owner/repo/agents"
|
|
441
|
+
)
|
|
442
|
+
print(message)
|
|
443
|
+
return CommandResult.error_result("No agent sources configured")
|
|
444
|
+
|
|
445
|
+
# Initialize git source manager
|
|
446
|
+
manager = GitSourceManager()
|
|
447
|
+
|
|
448
|
+
# Sync all configured sources (with timeout)
|
|
449
|
+
self.logger.info(f"Syncing {len(enabled_repos)} agent sources...")
|
|
450
|
+
sync_results = {}
|
|
451
|
+
|
|
452
|
+
for repo in enabled_repos:
|
|
453
|
+
try:
|
|
454
|
+
result = manager.sync_repository(repo, force=False)
|
|
455
|
+
sync_results[repo.identifier] = result
|
|
456
|
+
except Exception as e:
|
|
457
|
+
self.logger.warning(f"Failed to sync {repo.identifier}: {e}")
|
|
458
|
+
sync_results[repo.identifier] = {"synced": False, "error": str(e)}
|
|
459
|
+
|
|
460
|
+
# Get source filter from args
|
|
461
|
+
source_filter = getattr(args, "source", None)
|
|
462
|
+
|
|
463
|
+
# List all cached agents
|
|
464
|
+
all_agents = manager.list_cached_agents(repo_identifier=source_filter)
|
|
465
|
+
|
|
466
|
+
if not all_agents:
|
|
467
|
+
message = "No agents found in configured sources."
|
|
468
|
+
if sync_results:
|
|
469
|
+
failed_count = sum(
|
|
470
|
+
1 for r in sync_results.values() if not r.get("synced")
|
|
471
|
+
)
|
|
472
|
+
if failed_count > 0:
|
|
473
|
+
message += f"\n\n{failed_count} source(s) failed to sync. Check logs for details."
|
|
474
|
+
print(message)
|
|
475
|
+
return CommandResult.success_result(message, data={"agents": []})
|
|
476
|
+
|
|
477
|
+
# Format output
|
|
478
|
+
output_format = getattr(args, "format", "table")
|
|
479
|
+
|
|
480
|
+
if output_format == "json":
|
|
481
|
+
import json
|
|
482
|
+
|
|
483
|
+
print(json.dumps(all_agents, indent=2))
|
|
484
|
+
elif output_format == "simple":
|
|
485
|
+
for agent in all_agents:
|
|
486
|
+
name = agent.get("metadata", {}).get(
|
|
487
|
+
"name", agent.get("agent_id", "unknown")
|
|
488
|
+
)
|
|
489
|
+
repo = agent.get("repository", "unknown")
|
|
490
|
+
print(f"{name} (from {repo})")
|
|
491
|
+
else: # table format
|
|
492
|
+
print(f"\n{'Agent Name':<30} {'Repository':<40} {'Version':<15}")
|
|
493
|
+
print("=" * 85)
|
|
494
|
+
for agent in all_agents:
|
|
495
|
+
name = agent.get("metadata", {}).get(
|
|
496
|
+
"name", agent.get("agent_id", "unknown")
|
|
497
|
+
)
|
|
498
|
+
repo = agent.get("repository", "unknown")
|
|
499
|
+
version = agent.get("version", "unknown")[:12]
|
|
500
|
+
print(f"{name:<30} {repo:<40} {version:<15}")
|
|
501
|
+
print(
|
|
502
|
+
f"\nTotal: {len(all_agents)} agents from {len(sync_results)} sources"
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
return CommandResult.success_result(
|
|
506
|
+
f"Listed {len(all_agents)} agents from sources",
|
|
507
|
+
data={"agents": all_agents, "sync_results": sync_results},
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
except Exception as e:
|
|
511
|
+
self.logger.error(f"Error listing available agents: {e}", exc_info=True)
|
|
512
|
+
return CommandResult.error_result(f"Error listing available agents: {e}")
|
|
513
|
+
|
|
514
|
+
def _discover_agents(self, args) -> CommandResult:
|
|
515
|
+
"""Discover agents with rich filtering capabilities.
|
|
516
|
+
|
|
517
|
+
This command extends the 'available' command by adding semantic filtering
|
|
518
|
+
based on AUTO-DEPLOY-INDEX.md categories. Users can filter by category,
|
|
519
|
+
language, framework, platform, and specialization.
|
|
520
|
+
|
|
521
|
+
Design Decision: Delegate to agents_discover.py module
|
|
522
|
+
|
|
523
|
+
Rationale: Keep CLI command logic separate from routing logic for better
|
|
524
|
+
testability and maintainability. The discover_command function handles
|
|
525
|
+
all the complex filtering and formatting logic.
|
|
526
|
+
|
|
527
|
+
Args:
|
|
528
|
+
args: Command arguments with filter options:
|
|
529
|
+
- source: Source repository filter
|
|
530
|
+
- category: Category filter (e.g., 'engineer/backend')
|
|
531
|
+
- language: Language filter (e.g., 'python')
|
|
532
|
+
- framework: Framework filter (e.g., 'react')
|
|
533
|
+
- platform: Platform filter (e.g., 'vercel')
|
|
534
|
+
- specialization: Specialization filter (e.g., 'data')
|
|
535
|
+
- format: Output format (table, json, simple)
|
|
536
|
+
- verbose: Show descriptions and metadata
|
|
537
|
+
|
|
538
|
+
Returns:
|
|
539
|
+
CommandResult with filtered agent list or error
|
|
540
|
+
|
|
541
|
+
Example:
|
|
542
|
+
>>> # Called via: claude-mpm agents discover --category engineer/backend
|
|
543
|
+
>>> _discover_agents(args)
|
|
544
|
+
CommandResult(success=True, message="Discovered 8 agents")
|
|
545
|
+
"""
|
|
546
|
+
try:
|
|
547
|
+
from .agents_discover import discover_command
|
|
548
|
+
|
|
549
|
+
# Call discover_command and convert exit code to CommandResult
|
|
550
|
+
exit_code = discover_command(args)
|
|
551
|
+
|
|
552
|
+
if exit_code == 0:
|
|
553
|
+
return CommandResult.success_result("Agent discovery complete")
|
|
554
|
+
return CommandResult.error_result("Agent discovery failed")
|
|
555
|
+
|
|
556
|
+
except Exception as e:
|
|
557
|
+
self.logger.error(f"Error discovering agents: {e}", exc_info=True)
|
|
558
|
+
return CommandResult.error_result(f"Error discovering agents: {e}")
|
|
559
|
+
|
|
560
|
+
def _deploy_agents(self, args, force=False) -> CommandResult:
|
|
561
|
+
"""Deploy agents using two-phase sync: cache ā deploy.
|
|
562
|
+
|
|
563
|
+
Phase 3 Integration (1M-486): Uses Git sync service for deployment.
|
|
564
|
+
- Phase 1: Sync agents to ~/.claude-mpm/cache/agents/ (if needed)
|
|
565
|
+
- Phase 2: Deploy from cache to project .claude-mpm/agents/
|
|
566
|
+
|
|
567
|
+
This replaces the old single-tier deployment with a multi-project
|
|
568
|
+
architecture where one cache serves multiple project deployments.
|
|
569
|
+
"""
|
|
570
|
+
try:
|
|
571
|
+
# Handle preset deployment (uses different path)
|
|
572
|
+
if hasattr(args, "preset") and args.preset:
|
|
573
|
+
return self._deploy_preset(args)
|
|
574
|
+
|
|
575
|
+
from ...services.agents.sources.git_source_sync_service import (
|
|
576
|
+
GitSourceSyncService,
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
# Initialize git sync service
|
|
580
|
+
git_sync = GitSourceSyncService()
|
|
581
|
+
project_dir = Path.cwd()
|
|
582
|
+
|
|
583
|
+
self.logger.info("Phase 1: Syncing agents to cache...")
|
|
584
|
+
|
|
585
|
+
# Sync to cache (downloads from GitHub if needed)
|
|
586
|
+
sync_result = git_sync.sync_repository(force=force)
|
|
587
|
+
|
|
588
|
+
if not sync_result.get("synced"):
|
|
589
|
+
error_msg = sync_result.get("error", "Unknown sync error")
|
|
590
|
+
self.logger.error(f"Sync failed: {error_msg}")
|
|
591
|
+
return CommandResult.error_result(f"Sync failed: {error_msg}")
|
|
592
|
+
|
|
593
|
+
self.logger.info(
|
|
594
|
+
f"Phase 1 complete: {sync_result.get('agent_count', 0)} agents in cache"
|
|
595
|
+
)
|
|
596
|
+
self.logger.info(f"Phase 2: Deploying agents to {project_dir}...")
|
|
597
|
+
|
|
598
|
+
# Deploy from cache to project directory
|
|
599
|
+
deploy_result = git_sync.deploy_agents_to_project(
|
|
600
|
+
project_dir=project_dir,
|
|
601
|
+
agent_list=None, # Deploy all cached agents
|
|
602
|
+
force=force,
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
# Format combined results for output
|
|
606
|
+
combined_result = {
|
|
607
|
+
"deployed_count": len(deploy_result.get("deployed", []))
|
|
608
|
+
+ len(deploy_result.get("updated", [])),
|
|
609
|
+
"deployed": deploy_result.get("deployed", []),
|
|
610
|
+
"updated": deploy_result.get("updated", []),
|
|
611
|
+
"skipped": deploy_result.get("skipped", []),
|
|
612
|
+
"errors": deploy_result.get("failed", []),
|
|
613
|
+
"target_dir": deploy_result.get("deployment_dir", ""),
|
|
614
|
+
"sync_info": {
|
|
615
|
+
"cached_agents": sync_result.get("agent_count", 0),
|
|
616
|
+
"cache_dir": sync_result.get("cache_dir", ""),
|
|
617
|
+
},
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
output_format = self._get_output_format(args)
|
|
621
|
+
verbose = getattr(args, "verbose", False)
|
|
622
|
+
|
|
623
|
+
formatted = self._formatter.format_deployment_result(
|
|
624
|
+
combined_result, output_format=output_format, verbose=verbose
|
|
625
|
+
)
|
|
626
|
+
print(formatted)
|
|
627
|
+
|
|
628
|
+
success_count = len(deploy_result["deployed"]) + len(
|
|
629
|
+
deploy_result["updated"]
|
|
630
|
+
)
|
|
631
|
+
return CommandResult.success_result(
|
|
632
|
+
f"Deployed {success_count} agents from cache",
|
|
633
|
+
data={
|
|
634
|
+
"sync_result": sync_result,
|
|
635
|
+
"deploy_result": deploy_result,
|
|
636
|
+
"total_deployed": success_count,
|
|
637
|
+
},
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
except Exception as e:
|
|
641
|
+
self.logger.error(f"Error deploying agents: {e}", exc_info=True)
|
|
642
|
+
return CommandResult.error_result(f"Error deploying agents: {e}")
|
|
643
|
+
|
|
644
|
+
def _deploy_preset(self, args) -> CommandResult:
|
|
645
|
+
"""Deploy agents by preset name.
|
|
646
|
+
|
|
647
|
+
This method implements Phase 2 of the agents/skills CLI redesign,
|
|
648
|
+
enabling preset-based deployment like:
|
|
649
|
+
claude-mpm agents deploy --preset python-dev
|
|
650
|
+
|
|
651
|
+
Args:
|
|
652
|
+
args: Command arguments with preset name and optional flags
|
|
653
|
+
|
|
654
|
+
Returns:
|
|
655
|
+
CommandResult with deployment status
|
|
656
|
+
"""
|
|
657
|
+
try:
|
|
658
|
+
from pathlib import Path
|
|
659
|
+
|
|
660
|
+
from ...config.agent_sources import AgentSourceConfiguration
|
|
661
|
+
from ...services.agents.agent_preset_service import AgentPresetService
|
|
662
|
+
from ...services.agents.git_source_manager import GitSourceManager
|
|
663
|
+
from ...services.agents.single_tier_deployment_service import (
|
|
664
|
+
SingleTierDeploymentService,
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
preset_name = args.preset
|
|
668
|
+
dry_run = getattr(args, "dry_run", False)
|
|
669
|
+
|
|
670
|
+
# Initialize services
|
|
671
|
+
config = AgentSourceConfiguration.load()
|
|
672
|
+
deployment_dir = Path.home() / ".claude" / "agents"
|
|
673
|
+
git_source_manager = GitSourceManager()
|
|
674
|
+
preset_service = AgentPresetService(git_source_manager)
|
|
675
|
+
deployment_service = SingleTierDeploymentService(config, deployment_dir)
|
|
676
|
+
|
|
677
|
+
# Validate preset
|
|
678
|
+
if not preset_service.validate_preset(preset_name):
|
|
679
|
+
available = preset_service.list_presets()
|
|
680
|
+
print(f"ā Unknown preset: {preset_name}")
|
|
681
|
+
print("\nš Available presets:")
|
|
682
|
+
for preset in available:
|
|
683
|
+
print(
|
|
684
|
+
f" ⢠{preset['name']}: {preset['description']} ({preset['agent_count']} agents)"
|
|
685
|
+
)
|
|
686
|
+
print(f" Use cases: {', '.join(preset['use_cases'])}")
|
|
687
|
+
return CommandResult.error_result(f"Unknown preset: {preset_name}")
|
|
688
|
+
|
|
689
|
+
# Resolve preset to agent list
|
|
690
|
+
print(f"\nš Resolving preset: {preset_name}")
|
|
691
|
+
resolution = preset_service.resolve_agents(
|
|
692
|
+
preset_name, validate_availability=True
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
# Show preset info
|
|
696
|
+
preset_info = resolution["preset_info"]
|
|
697
|
+
print(f"\nšÆ Preset: {preset_info['description']}")
|
|
698
|
+
print(f" Agents: {preset_info['agent_count']}")
|
|
699
|
+
print(f" Use cases: {', '.join(preset_info['use_cases'])}")
|
|
700
|
+
|
|
701
|
+
# Show warnings for missing agents
|
|
702
|
+
if resolution["missing_agents"]:
|
|
703
|
+
print("\nā ļø Missing agents (not found in configured sources):")
|
|
704
|
+
for agent_id in resolution["missing_agents"]:
|
|
705
|
+
print(f" ⢠{agent_id}")
|
|
706
|
+
print("\nš” These agents are not available in your configured sources.")
|
|
707
|
+
print(" Deployment will continue with available agents.")
|
|
708
|
+
|
|
709
|
+
# Show conflicts
|
|
710
|
+
if resolution["conflicts"]:
|
|
711
|
+
print("\nā ļø Priority conflicts detected:")
|
|
712
|
+
for conflict in resolution["conflicts"]:
|
|
713
|
+
sources = ", ".join(conflict["sources"])
|
|
714
|
+
print(f" ⢠{conflict['agent_id']} (found in: {sources})")
|
|
715
|
+
print(" Using highest priority source for each")
|
|
716
|
+
|
|
717
|
+
# Dry run mode
|
|
718
|
+
if dry_run:
|
|
719
|
+
print("\nš DRY RUN: Preview agent deployment\n")
|
|
720
|
+
print("Agents to deploy:")
|
|
721
|
+
for agent in resolution["agents"]:
|
|
722
|
+
source = agent.get("source", "unknown")
|
|
723
|
+
print(f" ā {agent['agent_id']} (from {source})")
|
|
724
|
+
print(
|
|
725
|
+
"\nš” This is a dry run. Run without --dry-run to actually deploy."
|
|
726
|
+
)
|
|
727
|
+
return CommandResult.success_result(
|
|
728
|
+
"Dry run complete",
|
|
729
|
+
data={
|
|
730
|
+
"preset": preset_name,
|
|
731
|
+
"agents": resolution["agents"],
|
|
732
|
+
"missing": resolution["missing_agents"],
|
|
733
|
+
},
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
# Deploy agents
|
|
737
|
+
print(f"\nš¦ Deploying {len(resolution['agents'])} agents...")
|
|
738
|
+
deployed_count = 0
|
|
739
|
+
failed_count = 0
|
|
740
|
+
skipped_count = len(resolution["missing_agents"])
|
|
741
|
+
deployed_agents = []
|
|
742
|
+
failed_agents = []
|
|
743
|
+
|
|
744
|
+
for agent in resolution["agents"]:
|
|
745
|
+
agent_id = agent["agent_id"]
|
|
746
|
+
try:
|
|
747
|
+
# Deploy using single-tier deployment service
|
|
748
|
+
result = deployment_service.deploy_agent(
|
|
749
|
+
agent_id, source_repo=agent.get("source"), dry_run=False
|
|
750
|
+
)
|
|
751
|
+
|
|
752
|
+
if result.get("deployed"):
|
|
753
|
+
deployed_count += 1
|
|
754
|
+
deployed_agents.append(agent_id)
|
|
755
|
+
print(f" ā {agent_id}")
|
|
756
|
+
else:
|
|
757
|
+
failed_count += 1
|
|
758
|
+
failed_agents.append(
|
|
759
|
+
{
|
|
760
|
+
"agent_id": agent_id,
|
|
761
|
+
"error": result.get("error", "Unknown"),
|
|
762
|
+
}
|
|
763
|
+
)
|
|
764
|
+
print(f" ā {agent_id}: {result.get('error', 'Failed')}")
|
|
765
|
+
|
|
766
|
+
except Exception as e:
|
|
767
|
+
failed_count += 1
|
|
768
|
+
failed_agents.append({"agent_id": agent_id, "error": str(e)})
|
|
769
|
+
print(f" ā {agent_id}: {e}")
|
|
770
|
+
|
|
771
|
+
# Summary
|
|
772
|
+
print(f"\n{'=' * 60}")
|
|
773
|
+
print("š Deployment Summary")
|
|
774
|
+
print(f"{'=' * 60}")
|
|
775
|
+
print(f" ā
Deployed: {deployed_count}")
|
|
776
|
+
print(f" ā Failed: {failed_count}")
|
|
777
|
+
print(f" āļø Skipped: {skipped_count} (missing from sources)")
|
|
778
|
+
print(f"{'=' * 60}\n")
|
|
779
|
+
|
|
780
|
+
if failed_agents:
|
|
781
|
+
print("ā Failed agents:")
|
|
782
|
+
for failure in failed_agents:
|
|
783
|
+
print(f" ⢠{failure['agent_id']}: {failure['error']}")
|
|
784
|
+
print()
|
|
785
|
+
|
|
786
|
+
if deployed_count > 0:
|
|
787
|
+
print(f"ā
Successfully deployed {deployed_count} agents!")
|
|
788
|
+
return CommandResult.success_result(
|
|
789
|
+
f"Deployed {deployed_count} agents from preset '{preset_name}'",
|
|
790
|
+
data={
|
|
791
|
+
"preset": preset_name,
|
|
792
|
+
"deployed": deployed_agents,
|
|
793
|
+
"failed": failed_agents,
|
|
794
|
+
"skipped": resolution["missing_agents"],
|
|
795
|
+
},
|
|
796
|
+
)
|
|
797
|
+
return CommandResult.error_result(
|
|
798
|
+
f"No agents deployed from preset '{preset_name}'",
|
|
799
|
+
data={
|
|
800
|
+
"preset": preset_name,
|
|
801
|
+
"failed": failed_agents,
|
|
802
|
+
"skipped": resolution["missing_agents"],
|
|
803
|
+
},
|
|
804
|
+
)
|
|
805
|
+
|
|
806
|
+
except Exception as e:
|
|
807
|
+
self.logger.error(f"Error deploying preset: {e}", exc_info=True)
|
|
808
|
+
print(f"\nā Error deploying preset: {e}")
|
|
809
|
+
return CommandResult.error_result(f"Error deploying preset: {e}")
|
|
810
|
+
|
|
811
|
+
def _clean_agents(self, args) -> CommandResult:
|
|
812
|
+
"""Clean deployed agents."""
|
|
813
|
+
try:
|
|
814
|
+
result = self.cleanup_service.clean_deployed_agents()
|
|
815
|
+
|
|
816
|
+
output_format = self._get_output_format(args)
|
|
817
|
+
dry_run = False # Regular clean is not a dry run
|
|
818
|
+
|
|
819
|
+
formatted = self._formatter.format_cleanup_result(
|
|
820
|
+
result, output_format=output_format, dry_run=dry_run
|
|
821
|
+
)
|
|
822
|
+
print(formatted)
|
|
823
|
+
|
|
824
|
+
cleaned_count = result.get("cleaned_count", 0)
|
|
825
|
+
return CommandResult.success_result(
|
|
826
|
+
f"Cleaned {cleaned_count} agents", data=result
|
|
827
|
+
)
|
|
828
|
+
|
|
829
|
+
except Exception as e:
|
|
830
|
+
self.logger.error(f"Error cleaning agents: {e}", exc_info=True)
|
|
831
|
+
return CommandResult.error_result(f"Error cleaning agents: {e}")
|
|
832
|
+
|
|
833
|
+
def _view_agent(self, args) -> CommandResult:
|
|
834
|
+
"""View details of a specific agent."""
|
|
835
|
+
try:
|
|
836
|
+
agent_name = getattr(args, "agent_name", None)
|
|
837
|
+
if not agent_name:
|
|
838
|
+
return CommandResult.error_result(
|
|
839
|
+
"Agent name is required for view command"
|
|
840
|
+
)
|
|
841
|
+
|
|
842
|
+
# Get agent details from listing service
|
|
843
|
+
agent_details = self.listing_service.get_agent_details(agent_name)
|
|
844
|
+
|
|
845
|
+
if not agent_details:
|
|
846
|
+
# Try to find the agent to provide helpful error message
|
|
847
|
+
agent = self.listing_service.find_agent(agent_name)
|
|
848
|
+
if not agent:
|
|
849
|
+
return CommandResult.error_result(f"Agent '{agent_name}' not found")
|
|
850
|
+
return CommandResult.error_result(
|
|
851
|
+
f"Could not retrieve details for agent '{agent_name}'"
|
|
852
|
+
)
|
|
853
|
+
|
|
854
|
+
output_format = self._get_output_format(args)
|
|
855
|
+
verbose = getattr(args, "verbose", False)
|
|
856
|
+
|
|
857
|
+
formatted = self._formatter.format_agent_details(
|
|
858
|
+
agent_details, output_format=output_format, verbose=verbose
|
|
859
|
+
)
|
|
860
|
+
print(formatted)
|
|
861
|
+
|
|
862
|
+
return CommandResult.success_result(
|
|
863
|
+
f"Displayed details for {agent_name}", data=agent_details
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
except Exception as e:
|
|
867
|
+
self.logger.error(f"Error viewing agent: {e}", exc_info=True)
|
|
868
|
+
return CommandResult.error_result(f"Error viewing agent: {e}")
|
|
869
|
+
|
|
870
|
+
def _fix_agents(self, args) -> CommandResult:
|
|
871
|
+
"""Fix agent frontmatter issues using validation service."""
|
|
872
|
+
try:
|
|
873
|
+
dry_run = getattr(args, "dry_run", False)
|
|
874
|
+
agent_name = getattr(args, "agent_name", None)
|
|
875
|
+
fix_all = getattr(args, "all", False)
|
|
876
|
+
output_format = self._get_output_format(args)
|
|
877
|
+
|
|
878
|
+
# Route to appropriate handler based on input
|
|
879
|
+
if fix_all:
|
|
880
|
+
return self._fix_all_agents(dry_run, output_format)
|
|
881
|
+
if agent_name:
|
|
882
|
+
return self._fix_single_agent(agent_name, dry_run, output_format)
|
|
883
|
+
return self._handle_no_agent_specified(output_format)
|
|
884
|
+
|
|
885
|
+
except Exception as e:
|
|
886
|
+
self.logger.error(f"Error fixing agents: {e}", exc_info=True)
|
|
887
|
+
return CommandResult.error_result(f"Error fixing agents: {e}")
|
|
888
|
+
|
|
889
|
+
def _fix_all_agents(self, dry_run: bool, output_format: str) -> CommandResult:
|
|
890
|
+
"""Fix all agents' frontmatter issues."""
|
|
891
|
+
result = self.validation_service.fix_all_agents(dry_run=dry_run)
|
|
892
|
+
|
|
893
|
+
if self._is_structured_format(output_format):
|
|
894
|
+
self._print_structured_output(result, output_format)
|
|
102
895
|
else:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
896
|
+
self._print_all_agents_text_output(result, dry_run)
|
|
897
|
+
|
|
898
|
+
msg = f"{'Would fix' if dry_run else 'Fixed'} {result.get('total_corrections_available' if dry_run else 'total_corrections_made', 0)} issues"
|
|
899
|
+
return CommandResult.success_result(msg, data=result)
|
|
900
|
+
|
|
901
|
+
def _fix_single_agent(
|
|
902
|
+
self, agent_name: str, dry_run: bool, output_format: str
|
|
903
|
+
) -> CommandResult:
|
|
904
|
+
"""Fix a single agent's frontmatter issues."""
|
|
905
|
+
result = self.validation_service.fix_agent_frontmatter(
|
|
906
|
+
agent_name, dry_run=dry_run
|
|
907
|
+
)
|
|
908
|
+
|
|
909
|
+
if not result.get("success"):
|
|
910
|
+
return CommandResult.error_result(
|
|
911
|
+
result.get("error", "Failed to fix agent")
|
|
912
|
+
)
|
|
913
|
+
|
|
914
|
+
if self._is_structured_format(output_format):
|
|
915
|
+
self._print_structured_output(result, output_format)
|
|
916
|
+
else:
|
|
917
|
+
self._print_single_agent_text_output(agent_name, result, dry_run)
|
|
918
|
+
|
|
919
|
+
msg = f"{'Would fix' if dry_run else 'Fixed'} agent '{agent_name}'"
|
|
920
|
+
return CommandResult.success_result(msg, data=result)
|
|
921
|
+
|
|
922
|
+
def _handle_no_agent_specified(self, output_format: str) -> CommandResult:
|
|
923
|
+
"""Handle case where no agent is specified."""
|
|
924
|
+
usage_msg = "Please specify an agent name or use --all to fix all agents\nUsage: claude-mpm agents fix [agent_name] [--dry-run] [--all]"
|
|
925
|
+
if self._is_structured_format(output_format):
|
|
926
|
+
return CommandResult.error_result(
|
|
927
|
+
"No agent specified", data={"usage": usage_msg}
|
|
928
|
+
)
|
|
929
|
+
print(f"ā {usage_msg}")
|
|
930
|
+
return CommandResult.error_result("No agent specified")
|
|
931
|
+
|
|
932
|
+
def _print_structured_output(self, result: dict, output_format: str) -> None:
|
|
933
|
+
"""Print result in JSON or YAML format."""
|
|
934
|
+
formatted = (
|
|
935
|
+
self._formatter.format_as_json(result)
|
|
936
|
+
if str(output_format).lower() == OutputFormat.JSON
|
|
937
|
+
else self._formatter.format_as_yaml(result)
|
|
938
|
+
)
|
|
939
|
+
print(formatted)
|
|
940
|
+
|
|
941
|
+
def _print_all_agents_text_output(self, result: dict, dry_run: bool) -> None:
|
|
942
|
+
"""Print text output for all agents fix operation."""
|
|
943
|
+
mode = "DRY RUN" if dry_run else "FIX"
|
|
944
|
+
print(
|
|
945
|
+
f"\nš§ {mode}: Checking {result.get('total_agents', 0)} agent(s) for frontmatter issues...\n"
|
|
946
|
+
)
|
|
947
|
+
|
|
948
|
+
if result.get("results"):
|
|
949
|
+
for agent_result in result["results"]:
|
|
950
|
+
self._print_agent_result(agent_result, dry_run)
|
|
951
|
+
|
|
952
|
+
self._print_all_agents_summary(result, dry_run)
|
|
953
|
+
|
|
954
|
+
def _print_agent_result(self, agent_result: dict, dry_run: bool) -> None:
|
|
955
|
+
"""Print result for a single agent."""
|
|
956
|
+
print(f"š {agent_result['agent']}:")
|
|
957
|
+
if agent_result.get("skipped"):
|
|
958
|
+
print(f" ā ļø Skipped: {agent_result.get('reason', 'Unknown reason')}")
|
|
959
|
+
elif agent_result.get("was_valid"):
|
|
960
|
+
print(" ā No issues found")
|
|
961
|
+
else:
|
|
962
|
+
self._print_agent_issues(agent_result, dry_run)
|
|
963
|
+
print()
|
|
964
|
+
|
|
965
|
+
def _print_agent_issues(self, agent_result: dict, dry_run: bool) -> None:
|
|
966
|
+
"""Print issues found for an agent."""
|
|
967
|
+
if agent_result.get("errors_found", 0) > 0:
|
|
968
|
+
print(f" ā Errors found: {agent_result['errors_found']}")
|
|
969
|
+
if agent_result.get("warnings_found", 0) > 0:
|
|
970
|
+
print(f" ā ļø Warnings found: {agent_result['warnings_found']}")
|
|
971
|
+
|
|
972
|
+
if dry_run:
|
|
973
|
+
if agent_result.get("corrections_available", 0) > 0:
|
|
974
|
+
print(f" š§ Would fix: {agent_result['corrections_available']} issues")
|
|
975
|
+
elif agent_result.get("corrections_made", 0) > 0:
|
|
976
|
+
print(f" ā Fixed: {agent_result['corrections_made']} issues")
|
|
977
|
+
|
|
978
|
+
def _print_all_agents_summary(self, result: dict, dry_run: bool) -> None:
|
|
979
|
+
"""Print summary for all agents fix operation."""
|
|
980
|
+
print("=" * 80)
|
|
981
|
+
print("SUMMARY:")
|
|
982
|
+
print(f" Agents checked: {result.get('agents_checked', 0)}")
|
|
983
|
+
print(f" Total issues found: {result.get('total_issues_found', 0)}")
|
|
984
|
+
|
|
985
|
+
if dry_run:
|
|
986
|
+
print(
|
|
987
|
+
f" Issues that would be fixed: {result.get('total_corrections_available', 0)}"
|
|
988
|
+
)
|
|
989
|
+
print("\nš” Run without --dry-run to apply fixes")
|
|
990
|
+
else:
|
|
991
|
+
print(f" Issues fixed: {result.get('total_corrections_made', 0)}")
|
|
992
|
+
if result.get("total_corrections_made", 0) > 0:
|
|
993
|
+
print("\nā Frontmatter issues have been fixed!")
|
|
994
|
+
print("=" * 80 + "\n")
|
|
995
|
+
|
|
996
|
+
def _print_single_agent_text_output(
|
|
997
|
+
self, agent_name: str, result: dict, dry_run: bool
|
|
998
|
+
) -> None:
|
|
999
|
+
"""Print text output for single agent fix operation."""
|
|
1000
|
+
mode = "DRY RUN" if dry_run else "FIX"
|
|
1001
|
+
print(f"\nš§ {mode}: Checking agent '{agent_name}' for frontmatter issues...\n")
|
|
1002
|
+
|
|
1003
|
+
print(f"š {agent_name}:")
|
|
1004
|
+
if result.get("was_valid"):
|
|
1005
|
+
print(" ā No issues found")
|
|
1006
|
+
else:
|
|
1007
|
+
self._print_single_agent_issues(result, dry_run)
|
|
1008
|
+
print()
|
|
1009
|
+
|
|
1010
|
+
self._print_single_agent_footer(result, dry_run)
|
|
1011
|
+
|
|
1012
|
+
def _print_single_agent_issues(self, result: dict, dry_run: bool) -> None:
|
|
1013
|
+
"""Print issues for a single agent."""
|
|
1014
|
+
if result.get("errors_found"):
|
|
1015
|
+
print(" ā Errors:")
|
|
1016
|
+
for error in result["errors_found"]:
|
|
1017
|
+
print(f" - {error}")
|
|
1018
|
+
|
|
1019
|
+
if result.get("warnings_found"):
|
|
1020
|
+
print(" ā ļø Warnings:")
|
|
1021
|
+
for warning in result["warnings_found"]:
|
|
1022
|
+
print(f" - {warning}")
|
|
1023
|
+
|
|
1024
|
+
if dry_run:
|
|
1025
|
+
if result.get("corrections_available"):
|
|
1026
|
+
print(" š§ Would fix:")
|
|
1027
|
+
for correction in result["corrections_available"]:
|
|
1028
|
+
print(f" - {correction}")
|
|
1029
|
+
elif result.get("corrections_made"):
|
|
1030
|
+
print(" ā Fixed:")
|
|
1031
|
+
for correction in result["corrections_made"]:
|
|
1032
|
+
print(f" - {correction}")
|
|
1033
|
+
|
|
1034
|
+
def _print_single_agent_footer(self, result: dict, dry_run: bool) -> None:
|
|
1035
|
+
"""Print footer message for single agent fix."""
|
|
1036
|
+
if dry_run and result.get("corrections_available"):
|
|
1037
|
+
print("š” Run without --dry-run to apply fixes\n")
|
|
1038
|
+
elif not dry_run and result.get("corrections_made"):
|
|
1039
|
+
print("ā Frontmatter issues have been fixed!\n")
|
|
1040
|
+
|
|
1041
|
+
def _check_agent_dependencies(self, args) -> CommandResult:
|
|
1042
|
+
"""Check agent dependencies."""
|
|
1043
|
+
try:
|
|
1044
|
+
agent_name = getattr(args, "agent", None)
|
|
1045
|
+
result = self.dependency_service.check_dependencies(agent_name=agent_name)
|
|
1046
|
+
|
|
1047
|
+
if not result["success"]:
|
|
1048
|
+
if "available_agents" in result:
|
|
1049
|
+
print(f"ā Agent '{agent_name}' is not deployed")
|
|
1050
|
+
print(
|
|
1051
|
+
f" Available agents: {', '.join(result['available_agents'])}"
|
|
1052
|
+
)
|
|
1053
|
+
return CommandResult.error_result(
|
|
1054
|
+
result.get("error", "Dependency check failed")
|
|
1055
|
+
)
|
|
1056
|
+
|
|
1057
|
+
# Print the formatted report
|
|
1058
|
+
print(result["report"])
|
|
1059
|
+
|
|
1060
|
+
return CommandResult.success_result(
|
|
1061
|
+
"Dependency check completed", data=result
|
|
1062
|
+
)
|
|
1063
|
+
|
|
1064
|
+
except Exception as e:
|
|
1065
|
+
self.logger.error(f"Error checking dependencies: {e}", exc_info=True)
|
|
1066
|
+
return CommandResult.error_result(f"Error checking dependencies: {e}")
|
|
1067
|
+
|
|
1068
|
+
def _install_agent_dependencies(self, args) -> CommandResult:
|
|
1069
|
+
"""Install agent dependencies."""
|
|
1070
|
+
try:
|
|
1071
|
+
agent_name = getattr(args, "agent", None)
|
|
1072
|
+
dry_run = getattr(args, "dry_run", False)
|
|
1073
|
+
result = self.dependency_service.install_dependencies(
|
|
1074
|
+
agent_name=agent_name, dry_run=dry_run
|
|
1075
|
+
)
|
|
1076
|
+
|
|
1077
|
+
if not result["success"]:
|
|
1078
|
+
if "available_agents" in result:
|
|
1079
|
+
print(f"ā Agent '{agent_name}' is not deployed")
|
|
1080
|
+
print(
|
|
1081
|
+
f" Available agents: {', '.join(result['available_agents'])}"
|
|
1082
|
+
)
|
|
1083
|
+
return CommandResult.error_result(
|
|
1084
|
+
result.get("error", "Installation failed")
|
|
1085
|
+
)
|
|
1086
|
+
|
|
1087
|
+
if result.get("missing_count") == 0:
|
|
1088
|
+
print("ā
All Python dependencies are already installed")
|
|
1089
|
+
elif dry_run:
|
|
1090
|
+
print(
|
|
1091
|
+
f"Found {len(result['missing_dependencies'])} missing dependencies:"
|
|
1092
|
+
)
|
|
1093
|
+
for dep in result["missing_dependencies"]:
|
|
1094
|
+
print(f" - {dep}")
|
|
1095
|
+
print("\n--dry-run specified, not installing anything")
|
|
1096
|
+
print(f"Would install: {result['install_command']}")
|
|
1097
|
+
else:
|
|
1098
|
+
print(
|
|
1099
|
+
f"ā
Successfully installed {len(result.get('installed', []))} dependencies"
|
|
1100
|
+
)
|
|
1101
|
+
if result.get("still_missing"):
|
|
1102
|
+
print(
|
|
1103
|
+
f"ā ļø {len(result['still_missing'])} dependencies still missing after installation"
|
|
1104
|
+
)
|
|
1105
|
+
elif result.get("fully_resolved"):
|
|
1106
|
+
print("ā
All dependencies verified after installation")
|
|
1107
|
+
|
|
1108
|
+
return CommandResult.success_result(
|
|
1109
|
+
"Dependency installation completed", data=result
|
|
1110
|
+
)
|
|
1111
|
+
|
|
1112
|
+
except Exception as e:
|
|
1113
|
+
self.logger.error(f"Error installing dependencies: {e}", exc_info=True)
|
|
1114
|
+
return CommandResult.error_result(f"Error installing dependencies: {e}")
|
|
1115
|
+
|
|
1116
|
+
def _list_agent_dependencies(self, args) -> CommandResult:
|
|
1117
|
+
"""List agent dependencies."""
|
|
1118
|
+
try:
|
|
1119
|
+
output_format = self._get_output_format(args)
|
|
1120
|
+
result = self.dependency_service.list_dependencies(
|
|
1121
|
+
format_type=output_format
|
|
1122
|
+
)
|
|
1123
|
+
|
|
1124
|
+
if not result["success"]:
|
|
1125
|
+
return CommandResult.error_result(result.get("error", "Listing failed"))
|
|
1126
|
+
|
|
1127
|
+
# Format output based on requested format
|
|
1128
|
+
if output_format == "pip":
|
|
1129
|
+
for dep in result["dependencies"]:
|
|
1130
|
+
print(dep)
|
|
1131
|
+
elif str(output_format).lower() == OutputFormat.JSON:
|
|
1132
|
+
print(json.dumps(result["data"], indent=2))
|
|
1133
|
+
else: # text format
|
|
1134
|
+
print("=" * 60)
|
|
1135
|
+
print("DEPENDENCIES FROM DEPLOYED AGENTS")
|
|
1136
|
+
print("=" * 60)
|
|
108
1137
|
print()
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
print(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
1138
|
+
|
|
1139
|
+
if result["python_dependencies"]:
|
|
1140
|
+
print(
|
|
1141
|
+
f"Python Dependencies ({len(result['python_dependencies'])}):"
|
|
1142
|
+
)
|
|
1143
|
+
print("-" * 30)
|
|
1144
|
+
for dep in result["python_dependencies"]:
|
|
1145
|
+
print(f" {dep}")
|
|
1146
|
+
print()
|
|
1147
|
+
|
|
1148
|
+
if result["system_dependencies"]:
|
|
1149
|
+
print(
|
|
1150
|
+
f"System Dependencies ({len(result['system_dependencies'])}):"
|
|
1151
|
+
)
|
|
1152
|
+
print("-" * 30)
|
|
1153
|
+
for dep in result["system_dependencies"]:
|
|
1154
|
+
print(f" {dep}")
|
|
1155
|
+
print()
|
|
1156
|
+
|
|
1157
|
+
print("Per-Agent Dependencies:")
|
|
1158
|
+
print("-" * 30)
|
|
1159
|
+
for agent_id in sorted(result["per_agent"].keys()):
|
|
1160
|
+
deps = result["per_agent"][agent_id]
|
|
1161
|
+
python_count = len(deps.get("python", []))
|
|
1162
|
+
system_count = len(deps.get("system", []))
|
|
1163
|
+
if python_count or system_count:
|
|
1164
|
+
print(
|
|
1165
|
+
f" {agent_id}: {python_count} Python, {system_count} System"
|
|
1166
|
+
)
|
|
1167
|
+
|
|
1168
|
+
return CommandResult.success_result(
|
|
1169
|
+
"Dependency listing completed", data=result
|
|
1170
|
+
)
|
|
1171
|
+
|
|
1172
|
+
except Exception as e:
|
|
1173
|
+
self.logger.error(f"Error listing dependencies: {e}", exc_info=True)
|
|
1174
|
+
return CommandResult.error_result(f"Error listing dependencies: {e}")
|
|
1175
|
+
|
|
1176
|
+
def _fix_agent_dependencies(self, args) -> CommandResult:
|
|
1177
|
+
"""Fix agent dependency issues."""
|
|
1178
|
+
try:
|
|
1179
|
+
max_retries = getattr(args, "max_retries", 3)
|
|
1180
|
+
agent_name = getattr(args, "agent", None)
|
|
1181
|
+
|
|
1182
|
+
print("=" * 70)
|
|
1183
|
+
print("FIXING AGENT DEPENDENCIES WITH RETRY LOGIC")
|
|
1184
|
+
print("=" * 70)
|
|
1185
|
+
print()
|
|
1186
|
+
|
|
1187
|
+
result = self.dependency_service.fix_dependencies(
|
|
1188
|
+
max_retries=max_retries, agent_name=agent_name
|
|
1189
|
+
)
|
|
1190
|
+
|
|
1191
|
+
if not result["success"]:
|
|
1192
|
+
if "error" in result and "not deployed" in result["error"]:
|
|
1193
|
+
print(f"ā {result['error']}")
|
|
1194
|
+
return CommandResult.error_result(result.get("error", "Fix failed"))
|
|
1195
|
+
|
|
1196
|
+
if result.get("message") == "No deployed agents found":
|
|
1197
|
+
print("No deployed agents found")
|
|
1198
|
+
return CommandResult.success_result("No agents to fix")
|
|
1199
|
+
|
|
1200
|
+
if result.get("message") == "All dependencies are already satisfied":
|
|
1201
|
+
print("\nā
All dependencies are already satisfied!")
|
|
1202
|
+
return CommandResult.success_result("All dependencies satisfied")
|
|
1203
|
+
|
|
1204
|
+
# Show what's missing
|
|
1205
|
+
if result.get("missing_python"):
|
|
1206
|
+
print(f"\nā Missing Python packages: {len(result['missing_python'])}")
|
|
1207
|
+
for pkg in result["missing_python"][:10]:
|
|
1208
|
+
print(f" - {pkg}")
|
|
1209
|
+
if len(result["missing_python"]) > 10:
|
|
1210
|
+
print(f" ... and {len(result['missing_python']) - 10} more")
|
|
1211
|
+
|
|
1212
|
+
if result.get("missing_system"):
|
|
1213
|
+
print(f"\nā Missing system commands: {len(result['missing_system'])}")
|
|
1214
|
+
for cmd in result["missing_system"]:
|
|
1215
|
+
print(f" - {cmd}")
|
|
1216
|
+
print("\nā ļø System dependencies must be installed manually:")
|
|
1217
|
+
print(f" macOS: brew install {' '.join(result['missing_system'])}")
|
|
1218
|
+
print(f" Ubuntu: apt-get install {' '.join(result['missing_system'])}")
|
|
1219
|
+
|
|
1220
|
+
# Show incompatible packages
|
|
1221
|
+
if result.get("incompatible"):
|
|
1222
|
+
print(
|
|
1223
|
+
f"\nā ļø Skipping {len(result['incompatible'])} incompatible packages:"
|
|
1224
|
+
)
|
|
1225
|
+
for pkg in result["incompatible"][:5]:
|
|
1226
|
+
print(f" - {pkg}")
|
|
1227
|
+
if len(result["incompatible"]) > 5:
|
|
1228
|
+
print(f" ... and {len(result['incompatible']) - 5} more")
|
|
1229
|
+
|
|
1230
|
+
# Show installation results
|
|
1231
|
+
if result.get("fixed_python") or result.get("failed_python"):
|
|
1232
|
+
print("\n" + "=" * 70)
|
|
1233
|
+
print("INSTALLATION RESULTS:")
|
|
1234
|
+
print("=" * 70)
|
|
1235
|
+
|
|
1236
|
+
if result.get("fixed_python"):
|
|
1237
|
+
print(
|
|
1238
|
+
f"ā
Successfully installed: {len(result['fixed_python'])} packages"
|
|
1239
|
+
)
|
|
1240
|
+
|
|
1241
|
+
if result.get("failed_python"):
|
|
1242
|
+
print(
|
|
1243
|
+
f"ā Failed to install: {len(result['failed_python'])} packages"
|
|
1244
|
+
)
|
|
1245
|
+
errors = result.get("errors", {})
|
|
1246
|
+
for pkg in result["failed_python"]:
|
|
1247
|
+
print(f" - {pkg}: {errors.get(pkg, 'Unknown error')}")
|
|
1248
|
+
|
|
1249
|
+
# Final verification
|
|
1250
|
+
if result.get("still_missing") is not None:
|
|
1251
|
+
if not result["still_missing"]:
|
|
1252
|
+
print("\nā
All Python dependencies are now satisfied!")
|
|
1253
|
+
else:
|
|
1254
|
+
print(
|
|
1255
|
+
f"\nā ļø Still missing {len(result['still_missing'])} packages"
|
|
1256
|
+
)
|
|
1257
|
+
print("\nTry running again or install manually:")
|
|
1258
|
+
missing_sample = result["still_missing"][:3]
|
|
1259
|
+
print(f" pip install {' '.join(missing_sample)}")
|
|
1260
|
+
|
|
1261
|
+
print("\n" + "=" * 70)
|
|
1262
|
+
print("DONE")
|
|
1263
|
+
print("=" * 70)
|
|
1264
|
+
|
|
1265
|
+
return CommandResult.success_result("Dependency fix completed", data=result)
|
|
1266
|
+
|
|
1267
|
+
except Exception as e:
|
|
1268
|
+
self.logger.error(f"Error fixing dependencies: {e}", exc_info=True)
|
|
1269
|
+
return CommandResult.error_result(f"Error fixing dependencies: {e}")
|
|
1270
|
+
|
|
1271
|
+
def _handle_cleanup_command(self, args) -> CommandResult:
|
|
1272
|
+
"""Handle cleanup command with proper result wrapping."""
|
|
1273
|
+
exit_code = handle_agents_cleanup(args)
|
|
1274
|
+
return CommandResult(
|
|
1275
|
+
success=exit_code == 0,
|
|
1276
|
+
message=(
|
|
1277
|
+
"Agent cleanup complete" if exit_code == 0 else "Agent cleanup failed"
|
|
1278
|
+
),
|
|
161
1279
|
)
|
|
162
|
-
print(f"\nā Set Claude environment variables:")
|
|
163
|
-
for key, value in env_vars.items():
|
|
164
|
-
print(f" - {key}={value}")
|
|
165
1280
|
|
|
1281
|
+
def _cleanup_orphaned_agents(self, args) -> CommandResult:
|
|
1282
|
+
"""Clean up orphaned agents that don't have templates."""
|
|
1283
|
+
try:
|
|
1284
|
+
# Determine agents directory
|
|
1285
|
+
agents_dir = None
|
|
1286
|
+
if hasattr(args, "agents_dir") and args.agents_dir:
|
|
1287
|
+
agents_dir = args.agents_dir
|
|
1288
|
+
|
|
1289
|
+
# Determine if we're doing a dry run
|
|
1290
|
+
dry_run = getattr(args, "dry_run", True)
|
|
1291
|
+
if hasattr(args, "force") and args.force:
|
|
1292
|
+
dry_run = False
|
|
1293
|
+
|
|
1294
|
+
# Perform cleanup using the cleanup service
|
|
1295
|
+
results = self.cleanup_service.clean_orphaned_agents(
|
|
1296
|
+
agents_dir=agents_dir, dry_run=dry_run
|
|
1297
|
+
)
|
|
1298
|
+
|
|
1299
|
+
output_format = self._get_output_format(args)
|
|
1300
|
+
|
|
1301
|
+
formatted = self._formatter.format_cleanup_result(
|
|
1302
|
+
results, output_format=output_format, dry_run=dry_run
|
|
1303
|
+
)
|
|
1304
|
+
print(formatted)
|
|
1305
|
+
|
|
1306
|
+
# Determine success/error based on results
|
|
1307
|
+
if results.get("errors") and not dry_run:
|
|
1308
|
+
return CommandResult.error_result(
|
|
1309
|
+
f"Cleanup completed with {len(results['errors'])} errors",
|
|
1310
|
+
data=results,
|
|
1311
|
+
)
|
|
1312
|
+
|
|
1313
|
+
return CommandResult.success_result(
|
|
1314
|
+
f"Cleanup {'preview' if dry_run else 'completed'}", data=results
|
|
1315
|
+
)
|
|
1316
|
+
|
|
1317
|
+
except Exception as e:
|
|
1318
|
+
self.logger.error(f"Error during cleanup: {e}", exc_info=True)
|
|
1319
|
+
return CommandResult.error_result(f"Error during cleanup: {e}")
|
|
1320
|
+
|
|
1321
|
+
def _create_local_agent(self, args) -> CommandResult:
|
|
1322
|
+
"""Create a new local agent template."""
|
|
1323
|
+
try:
|
|
1324
|
+
if getattr(args, "interactive", False):
|
|
1325
|
+
# Launch interactive wizard
|
|
1326
|
+
from ..interactive.agent_wizard import run_interactive_agent_wizard
|
|
1327
|
+
|
|
1328
|
+
exit_code = run_interactive_agent_wizard()
|
|
1329
|
+
if exit_code == 0:
|
|
1330
|
+
return CommandResult.success_result("Agent created successfully")
|
|
1331
|
+
return CommandResult.error_result("Agent creation cancelled or failed")
|
|
1332
|
+
|
|
1333
|
+
# Non-interactive creation
|
|
1334
|
+
from ...services.agents.local_template_manager import (
|
|
1335
|
+
LocalAgentTemplateManager,
|
|
1336
|
+
)
|
|
1337
|
+
|
|
1338
|
+
agent_id = getattr(args, "agent_id", None)
|
|
1339
|
+
if not agent_id:
|
|
1340
|
+
return CommandResult.error_result(
|
|
1341
|
+
"--agent-id is required for non-interactive creation"
|
|
1342
|
+
)
|
|
1343
|
+
|
|
1344
|
+
manager = LocalAgentTemplateManager()
|
|
1345
|
+
name = getattr(args, "name", agent_id.replace("-", " ").title())
|
|
1346
|
+
model = getattr(args, "model", "sonnet")
|
|
1347
|
+
inherit_from = getattr(args, "inherit_from", None)
|
|
1348
|
+
|
|
1349
|
+
# Create basic template
|
|
1350
|
+
template = manager.create_local_template(
|
|
1351
|
+
agent_id=agent_id,
|
|
1352
|
+
name=name,
|
|
1353
|
+
description=f"Local agent: {name}",
|
|
1354
|
+
instructions="# Agent Instructions\n\nCustomize this agent's behavior here.",
|
|
1355
|
+
model=model,
|
|
1356
|
+
parent_agent=inherit_from,
|
|
1357
|
+
tier="project",
|
|
1358
|
+
)
|
|
1359
|
+
|
|
1360
|
+
if template:
|
|
1361
|
+
return CommandResult.success_result(
|
|
1362
|
+
f"Created local agent '{agent_id}' in .claude-mpm/agents/",
|
|
1363
|
+
data={
|
|
1364
|
+
"agent_id": agent_id,
|
|
1365
|
+
"path": f".claude-mpm/agents/{agent_id}.json",
|
|
1366
|
+
},
|
|
1367
|
+
)
|
|
1368
|
+
return CommandResult.error_result("Failed to create agent template")
|
|
1369
|
+
|
|
1370
|
+
except Exception as e:
|
|
1371
|
+
self.logger.error(f"Error creating local agent: {e}", exc_info=True)
|
|
1372
|
+
return CommandResult.error_result(f"Error creating local agent: {e}")
|
|
1373
|
+
|
|
1374
|
+
def _edit_local_agent(self, args) -> CommandResult:
|
|
1375
|
+
"""Edit a local agent template."""
|
|
1376
|
+
try:
|
|
1377
|
+
agent_id = getattr(args, "agent_id", None)
|
|
1378
|
+
if not agent_id:
|
|
1379
|
+
return CommandResult.error_result("agent_id is required")
|
|
166
1380
|
|
|
167
|
-
|
|
1381
|
+
import os
|
|
1382
|
+
import subprocess
|
|
1383
|
+
|
|
1384
|
+
from ...services.agents.local_template_manager import (
|
|
1385
|
+
LocalAgentTemplateManager,
|
|
1386
|
+
)
|
|
1387
|
+
|
|
1388
|
+
manager = LocalAgentTemplateManager()
|
|
1389
|
+
template = manager.get_local_template(agent_id)
|
|
1390
|
+
|
|
1391
|
+
if not template:
|
|
1392
|
+
return CommandResult.error_result(f"Local agent '{agent_id}' not found")
|
|
1393
|
+
|
|
1394
|
+
# Get template file path
|
|
1395
|
+
template_file = None
|
|
1396
|
+
if template.tier == "project":
|
|
1397
|
+
template_file = manager.project_agents_dir / f"{agent_id}.json"
|
|
1398
|
+
else:
|
|
1399
|
+
template_file = manager.user_agents_dir / f"{agent_id}.json"
|
|
1400
|
+
|
|
1401
|
+
if not template_file or not template_file.exists():
|
|
1402
|
+
return CommandResult.error_result(
|
|
1403
|
+
f"Template file not found for '{agent_id}'"
|
|
1404
|
+
)
|
|
1405
|
+
|
|
1406
|
+
if getattr(args, "interactive", False):
|
|
1407
|
+
# Launch interactive editor
|
|
1408
|
+
from ..interactive.agent_wizard import AgentWizard
|
|
1409
|
+
|
|
1410
|
+
wizard = AgentWizard()
|
|
1411
|
+
success, message = wizard._edit_agent_config(template)
|
|
1412
|
+
if success:
|
|
1413
|
+
return CommandResult.success_result(message)
|
|
1414
|
+
return CommandResult.error_result(message)
|
|
1415
|
+
|
|
1416
|
+
# Use system editor
|
|
1417
|
+
editor = getattr(args, "editor", None) or os.environ.get("EDITOR", "nano")
|
|
1418
|
+
subprocess.run([editor, str(template_file)], check=True)
|
|
1419
|
+
return CommandResult.success_result(
|
|
1420
|
+
f"Agent '{agent_id}' edited successfully"
|
|
1421
|
+
)
|
|
1422
|
+
|
|
1423
|
+
except subprocess.CalledProcessError:
|
|
1424
|
+
return CommandResult.error_result("Editor exited with error")
|
|
1425
|
+
except Exception as e:
|
|
1426
|
+
self.logger.error(f"Error editing local agent: {e}", exc_info=True)
|
|
1427
|
+
return CommandResult.error_result(f"Error editing local agent: {e}")
|
|
1428
|
+
|
|
1429
|
+
def _delete_local_agent(self, args) -> CommandResult:
|
|
1430
|
+
"""Delete local agent templates."""
|
|
1431
|
+
try:
|
|
1432
|
+
agent_ids = getattr(args, "agent_ids", [])
|
|
1433
|
+
if not agent_ids:
|
|
1434
|
+
return CommandResult.error_result("No agent IDs specified")
|
|
1435
|
+
|
|
1436
|
+
from ...services.agents.local_template_manager import (
|
|
1437
|
+
LocalAgentTemplateManager,
|
|
1438
|
+
)
|
|
1439
|
+
|
|
1440
|
+
manager = LocalAgentTemplateManager()
|
|
1441
|
+
force = getattr(args, "force", False)
|
|
1442
|
+
keep_deployment = getattr(args, "keep_deployment", False)
|
|
1443
|
+
backup = getattr(args, "backup", False)
|
|
1444
|
+
|
|
1445
|
+
# Confirmation if not forced
|
|
1446
|
+
if not force:
|
|
1447
|
+
print(f"\nā ļø This will delete {len(agent_ids)} agent(s):")
|
|
1448
|
+
for agent_id in agent_ids:
|
|
1449
|
+
print(f" - {agent_id}")
|
|
1450
|
+
confirm = input("\nAre you sure? [y/N]: ").strip().lower()
|
|
1451
|
+
if confirm not in ["y", "yes"]:
|
|
1452
|
+
return CommandResult.error_result("Deletion cancelled")
|
|
1453
|
+
|
|
1454
|
+
# Delete agents
|
|
1455
|
+
if len(agent_ids) == 1:
|
|
1456
|
+
result = manager.delete_local_template(
|
|
1457
|
+
agent_id=agent_ids[0],
|
|
1458
|
+
tier="all",
|
|
1459
|
+
delete_deployment=not keep_deployment,
|
|
1460
|
+
backup_first=backup,
|
|
1461
|
+
)
|
|
1462
|
+
if result["success"]:
|
|
1463
|
+
message = f"Successfully deleted agent '{agent_ids[0]}'"
|
|
1464
|
+
if result["backup_location"]:
|
|
1465
|
+
message += f"\nBackup saved to: {result['backup_location']}"
|
|
1466
|
+
return CommandResult.success_result(message, data=result)
|
|
1467
|
+
return CommandResult.error_result(
|
|
1468
|
+
f"Failed to delete agent: {', '.join(result['errors'])}"
|
|
1469
|
+
)
|
|
1470
|
+
results = manager.delete_multiple_templates(
|
|
1471
|
+
agent_ids=agent_ids,
|
|
1472
|
+
tier="all",
|
|
1473
|
+
delete_deployment=not keep_deployment,
|
|
1474
|
+
backup_first=backup,
|
|
1475
|
+
)
|
|
1476
|
+
|
|
1477
|
+
message = ""
|
|
1478
|
+
if results["successful"]:
|
|
1479
|
+
message = (
|
|
1480
|
+
f"Successfully deleted {len(results['successful'])} agent(s):\n"
|
|
1481
|
+
)
|
|
1482
|
+
for agent_id in results["successful"]:
|
|
1483
|
+
message += f" - {agent_id}\n"
|
|
1484
|
+
|
|
1485
|
+
if results["failed"]:
|
|
1486
|
+
if message:
|
|
1487
|
+
message += "\n"
|
|
1488
|
+
message += f"Failed to delete {len(results['failed'])} agent(s):\n"
|
|
1489
|
+
for agent_id in results["failed"]:
|
|
1490
|
+
errors = results["details"][agent_id]["errors"]
|
|
1491
|
+
message += f" - {agent_id}: {', '.join(errors)}\n"
|
|
1492
|
+
|
|
1493
|
+
if results["successful"]:
|
|
1494
|
+
return CommandResult.success_result(message.strip(), data=results)
|
|
1495
|
+
return CommandResult.error_result(message.strip(), data=results)
|
|
1496
|
+
|
|
1497
|
+
except Exception as e:
|
|
1498
|
+
self.logger.error(f"Error deleting local agents: {e}", exc_info=True)
|
|
1499
|
+
return CommandResult.error_result(f"Error deleting local agents: {e}")
|
|
1500
|
+
|
|
1501
|
+
def _manage_local_agents(self, args) -> CommandResult:
|
|
1502
|
+
"""Redirect to main configuration interface (DEPRECATED)."""
|
|
1503
|
+
try:
|
|
1504
|
+
from rich.console import Console
|
|
1505
|
+
from rich.prompt import Confirm
|
|
1506
|
+
|
|
1507
|
+
console = Console()
|
|
1508
|
+
|
|
1509
|
+
console.print(
|
|
1510
|
+
"\n[bold cyan]āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā®[/bold cyan]"
|
|
1511
|
+
)
|
|
1512
|
+
console.print(
|
|
1513
|
+
"[bold cyan]ā Agent Management Has Moved! ā[/bold cyan]"
|
|
1514
|
+
)
|
|
1515
|
+
console.print(
|
|
1516
|
+
"[bold cyan]ā°āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāÆ[/bold cyan]\n"
|
|
1517
|
+
)
|
|
1518
|
+
|
|
1519
|
+
console.print("For a better experience with integrated configuration:")
|
|
1520
|
+
console.print(" ⢠Agent management")
|
|
1521
|
+
console.print(" ⢠Skills management")
|
|
1522
|
+
console.print(" ⢠Template editing")
|
|
1523
|
+
console.print(" ⢠Behavior configuration")
|
|
1524
|
+
console.print(" ⢠Startup settings\n")
|
|
1525
|
+
|
|
1526
|
+
console.print("Please use: [bold green]claude-mpm config[/bold green]\n")
|
|
1527
|
+
|
|
1528
|
+
if Confirm.ask("Launch configuration interface now?", default=True):
|
|
1529
|
+
# Import and run config command directly
|
|
1530
|
+
from claude_mpm.cli.commands.configure import ConfigureCommand
|
|
1531
|
+
|
|
1532
|
+
config_cmd = ConfigureCommand()
|
|
1533
|
+
return config_cmd.execute(args)
|
|
1534
|
+
console.print(
|
|
1535
|
+
"\n[dim]Run 'claude-mpm config' anytime to access agent management[/dim]"
|
|
1536
|
+
)
|
|
1537
|
+
return CommandResult.success_result("Redirected to config interface")
|
|
1538
|
+
|
|
1539
|
+
except Exception as e:
|
|
1540
|
+
self.logger.error(f"Error redirecting to config: {e}", exc_info=True)
|
|
1541
|
+
return CommandResult.error_result(f"Error redirecting to config: {e}")
|
|
1542
|
+
|
|
1543
|
+
def _configure_deployment(self, args) -> CommandResult:
|
|
1544
|
+
"""Configure agent deployment settings."""
|
|
1545
|
+
try:
|
|
1546
|
+
from pathlib import Path
|
|
1547
|
+
|
|
1548
|
+
import yaml
|
|
1549
|
+
|
|
1550
|
+
from claude_mpm.core.config import Config
|
|
1551
|
+
|
|
1552
|
+
config = Config()
|
|
1553
|
+
config_path = Path.cwd() / ".claude-mpm" / "configuration.yaml"
|
|
1554
|
+
|
|
1555
|
+
# Handle show command
|
|
1556
|
+
if getattr(args, "show", False):
|
|
1557
|
+
from ...services.agents.deployment.deployment_config_loader import (
|
|
1558
|
+
DeploymentConfigLoader,
|
|
1559
|
+
)
|
|
1560
|
+
|
|
1561
|
+
loader = DeploymentConfigLoader(self.logger)
|
|
1562
|
+
settings = loader.get_deployment_settings(config)
|
|
1563
|
+
|
|
1564
|
+
print("\nš Agent Deployment Configuration")
|
|
1565
|
+
print("=" * 50)
|
|
1566
|
+
print(f"Configuration file: {config_path}")
|
|
1567
|
+
print("\nš§ Deployment Settings:")
|
|
1568
|
+
print(f" Deploy system agents: {settings['deploy_system_agents']}")
|
|
1569
|
+
print(f" Deploy local agents: {settings['deploy_local_agents']}")
|
|
1570
|
+
print(f" Deploy user agents: {settings['deploy_user_agents']}")
|
|
1571
|
+
print(
|
|
1572
|
+
f" Prefer local over system: {settings['prefer_local_over_system']}"
|
|
1573
|
+
)
|
|
1574
|
+
print(f" Version comparison: {settings['version_comparison']}")
|
|
1575
|
+
|
|
1576
|
+
if settings["enabled_agents"]:
|
|
1577
|
+
print(
|
|
1578
|
+
f"\nā
Enabled agents: {', '.join(settings['enabled_agents'])}"
|
|
1579
|
+
)
|
|
1580
|
+
else:
|
|
1581
|
+
print("\nā
Enabled agents: All (no restrictions)")
|
|
1582
|
+
|
|
1583
|
+
if settings["disabled_agents"]:
|
|
1584
|
+
print(
|
|
1585
|
+
f"ā Disabled agents: {', '.join(settings['disabled_agents'])}"
|
|
1586
|
+
)
|
|
1587
|
+
else:
|
|
1588
|
+
print("ā Disabled agents: None")
|
|
1589
|
+
|
|
1590
|
+
print("\n" + "=" * 50)
|
|
1591
|
+
return CommandResult.success_result(
|
|
1592
|
+
"Displayed deployment configuration"
|
|
1593
|
+
)
|
|
1594
|
+
|
|
1595
|
+
# Handle interactive mode
|
|
1596
|
+
if getattr(args, "interactive", False):
|
|
1597
|
+
return self._configure_deployment_interactive(config_path)
|
|
1598
|
+
|
|
1599
|
+
# Load current configuration
|
|
1600
|
+
if not config_path.exists():
|
|
1601
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1602
|
+
config_data = {}
|
|
1603
|
+
else:
|
|
1604
|
+
with config_path.open() as f:
|
|
1605
|
+
config_data = yaml.safe_load(f) or {}
|
|
1606
|
+
|
|
1607
|
+
# Ensure agent_deployment section exists
|
|
1608
|
+
if "agent_deployment" not in config_data:
|
|
1609
|
+
config_data["agent_deployment"] = {}
|
|
1610
|
+
|
|
1611
|
+
modified = False
|
|
1612
|
+
|
|
1613
|
+
# Handle enable/disable operations
|
|
1614
|
+
if getattr(args, "enable_all", False):
|
|
1615
|
+
config_data["agent_deployment"]["enabled_agents"] = []
|
|
1616
|
+
config_data["agent_deployment"]["disabled_agents"] = []
|
|
1617
|
+
print("ā
Enabled all agents for deployment")
|
|
1618
|
+
modified = True
|
|
1619
|
+
|
|
1620
|
+
if getattr(args, "enable_system", False):
|
|
1621
|
+
config_data["agent_deployment"]["deploy_system_agents"] = True
|
|
1622
|
+
print("ā
Enabled system agents for deployment")
|
|
1623
|
+
modified = True
|
|
1624
|
+
|
|
1625
|
+
if getattr(args, "disable_system", False):
|
|
1626
|
+
config_data["agent_deployment"]["deploy_system_agents"] = False
|
|
1627
|
+
print("ā Disabled system agents from deployment")
|
|
1628
|
+
modified = True
|
|
1629
|
+
|
|
1630
|
+
if getattr(args, "enable_local", False):
|
|
1631
|
+
config_data["agent_deployment"]["deploy_local_agents"] = True
|
|
1632
|
+
print("ā
Enabled local agents for deployment")
|
|
1633
|
+
modified = True
|
|
1634
|
+
|
|
1635
|
+
if getattr(args, "disable_local", False):
|
|
1636
|
+
config_data["agent_deployment"]["deploy_local_agents"] = False
|
|
1637
|
+
print("ā Disabled local agents from deployment")
|
|
1638
|
+
modified = True
|
|
1639
|
+
|
|
1640
|
+
if getattr(args, "enable", None):
|
|
1641
|
+
enabled = config_data["agent_deployment"].get("enabled_agents", [])
|
|
1642
|
+
disabled = config_data["agent_deployment"].get("disabled_agents", [])
|
|
1643
|
+
|
|
1644
|
+
for agent_id in args.enable:
|
|
1645
|
+
if agent_id not in enabled:
|
|
1646
|
+
enabled.append(agent_id)
|
|
1647
|
+
if agent_id in disabled:
|
|
1648
|
+
disabled.remove(agent_id)
|
|
1649
|
+
|
|
1650
|
+
config_data["agent_deployment"]["enabled_agents"] = enabled
|
|
1651
|
+
config_data["agent_deployment"]["disabled_agents"] = disabled
|
|
1652
|
+
print(f"ā
Enabled agents: {', '.join(args.enable)}")
|
|
1653
|
+
modified = True
|
|
1654
|
+
|
|
1655
|
+
if getattr(args, "disable", None):
|
|
1656
|
+
disabled = config_data["agent_deployment"].get("disabled_agents", [])
|
|
1657
|
+
|
|
1658
|
+
for agent_id in args.disable:
|
|
1659
|
+
if agent_id not in disabled:
|
|
1660
|
+
disabled.append(agent_id)
|
|
1661
|
+
|
|
1662
|
+
config_data["agent_deployment"]["disabled_agents"] = disabled
|
|
1663
|
+
print(f"ā Disabled agents: {', '.join(args.disable)}")
|
|
1664
|
+
modified = True
|
|
1665
|
+
|
|
1666
|
+
# Save configuration if modified
|
|
1667
|
+
if modified:
|
|
1668
|
+
with config_path.open("w") as f:
|
|
1669
|
+
yaml.dump(config_data, f, default_flow_style=False, sort_keys=False)
|
|
1670
|
+
print(f"\nš¾ Configuration saved to {config_path}")
|
|
1671
|
+
return CommandResult.success_result("Deployment configuration updated")
|
|
1672
|
+
|
|
1673
|
+
# If no modifications were made and not showing, display help
|
|
1674
|
+
if not getattr(args, "show", False):
|
|
1675
|
+
print("No configuration changes specified. Use --help for options.")
|
|
1676
|
+
return CommandResult.success_result("No changes made")
|
|
1677
|
+
|
|
1678
|
+
except Exception as e:
|
|
1679
|
+
self.logger.error(f"Error configuring deployment: {e}", exc_info=True)
|
|
1680
|
+
return CommandResult.error_result(f"Error configuring deployment: {e}")
|
|
1681
|
+
|
|
1682
|
+
def _configure_deployment_interactive(self, config_path: Path) -> CommandResult:
|
|
1683
|
+
"""Interactive mode for configuring agent deployment."""
|
|
1684
|
+
try:
|
|
1685
|
+
import yaml
|
|
1686
|
+
|
|
1687
|
+
from ...utils.ui_helpers import (
|
|
1688
|
+
prompt_choice,
|
|
1689
|
+
prompt_multiselect,
|
|
1690
|
+
prompt_yes_no,
|
|
1691
|
+
)
|
|
1692
|
+
|
|
1693
|
+
# Load current configuration
|
|
1694
|
+
if config_path.exists():
|
|
1695
|
+
with config_path.open() as f:
|
|
1696
|
+
config_data = yaml.safe_load(f) or {}
|
|
1697
|
+
else:
|
|
1698
|
+
config_data = {}
|
|
1699
|
+
|
|
1700
|
+
if "agent_deployment" not in config_data:
|
|
1701
|
+
config_data["agent_deployment"] = {}
|
|
1702
|
+
|
|
1703
|
+
settings = config_data["agent_deployment"]
|
|
1704
|
+
|
|
1705
|
+
print("\nš® Interactive Agent Deployment Configuration")
|
|
1706
|
+
print("=" * 50)
|
|
1707
|
+
|
|
1708
|
+
# Configure source types
|
|
1709
|
+
settings["deploy_system_agents"] = prompt_yes_no(
|
|
1710
|
+
"Deploy system agents?",
|
|
1711
|
+
default=settings.get("deploy_system_agents", True),
|
|
1712
|
+
)
|
|
1713
|
+
|
|
1714
|
+
settings["deploy_local_agents"] = prompt_yes_no(
|
|
1715
|
+
"Deploy local project agents?",
|
|
1716
|
+
default=settings.get("deploy_local_agents", True),
|
|
1717
|
+
)
|
|
1718
|
+
|
|
1719
|
+
settings["deploy_user_agents"] = prompt_yes_no(
|
|
1720
|
+
"Deploy user-level agents?",
|
|
1721
|
+
default=settings.get("deploy_user_agents", True),
|
|
1722
|
+
)
|
|
1723
|
+
|
|
1724
|
+
# Configure version behavior
|
|
1725
|
+
settings["prefer_local_over_system"] = prompt_yes_no(
|
|
1726
|
+
"Should local agents override system agents with same ID?",
|
|
1727
|
+
default=settings.get("prefer_local_over_system", True),
|
|
1728
|
+
)
|
|
1729
|
+
|
|
1730
|
+
settings["version_comparison"] = prompt_yes_no(
|
|
1731
|
+
"Compare versions across sources and deploy highest?",
|
|
1732
|
+
default=settings.get("version_comparison", True),
|
|
1733
|
+
)
|
|
1734
|
+
|
|
1735
|
+
# Configure specific agents
|
|
1736
|
+
choice = prompt_choice(
|
|
1737
|
+
"How would you like to configure specific agents?",
|
|
1738
|
+
[
|
|
1739
|
+
"No restrictions (all agents enabled)",
|
|
1740
|
+
"Specify disabled agents",
|
|
1741
|
+
"Specify enabled agents only",
|
|
1742
|
+
],
|
|
1743
|
+
)
|
|
1744
|
+
|
|
1745
|
+
if choice == "No restrictions (all agents enabled)":
|
|
1746
|
+
settings["enabled_agents"] = []
|
|
1747
|
+
settings["disabled_agents"] = []
|
|
1748
|
+
elif choice == "Specify disabled agents":
|
|
1749
|
+
# Get list of available agents
|
|
1750
|
+
from ...services.agents.listing_service import AgentListingService
|
|
1751
|
+
|
|
1752
|
+
listing_service = AgentListingService()
|
|
1753
|
+
agents, _ = listing_service.list_all_agents()
|
|
1754
|
+
agent_ids = sorted({agent.name for agent in agents})
|
|
1755
|
+
|
|
1756
|
+
if agent_ids:
|
|
1757
|
+
disabled = prompt_multiselect(
|
|
1758
|
+
"Select agents to disable:",
|
|
1759
|
+
agent_ids,
|
|
1760
|
+
default=settings.get("disabled_agents", []),
|
|
1761
|
+
)
|
|
1762
|
+
settings["disabled_agents"] = disabled
|
|
1763
|
+
settings["enabled_agents"] = []
|
|
1764
|
+
else:
|
|
1765
|
+
print("No agents found to configure")
|
|
1766
|
+
else: # Specify enabled agents only
|
|
1767
|
+
from ...services.agents.listing_service import AgentListingService
|
|
1768
|
+
|
|
1769
|
+
listing_service = AgentListingService()
|
|
1770
|
+
agents, _ = listing_service.list_all_agents()
|
|
1771
|
+
agent_ids = sorted({agent.name for agent in agents})
|
|
1772
|
+
|
|
1773
|
+
if agent_ids:
|
|
1774
|
+
enabled = prompt_multiselect(
|
|
1775
|
+
"Select agents to enable (others will be disabled):",
|
|
1776
|
+
agent_ids,
|
|
1777
|
+
default=settings.get("enabled_agents", []),
|
|
1778
|
+
)
|
|
1779
|
+
settings["enabled_agents"] = enabled
|
|
1780
|
+
settings["disabled_agents"] = []
|
|
1781
|
+
else:
|
|
1782
|
+
print("No agents found to configure")
|
|
1783
|
+
|
|
1784
|
+
# Save configuration
|
|
1785
|
+
config_data["agent_deployment"] = settings
|
|
1786
|
+
|
|
1787
|
+
# Ensure parent directory exists
|
|
1788
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1789
|
+
|
|
1790
|
+
with config_path.open("w") as f:
|
|
1791
|
+
yaml.dump(config_data, f, default_flow_style=False, sort_keys=False)
|
|
1792
|
+
|
|
1793
|
+
print(f"\nā
Configuration saved to {config_path}")
|
|
1794
|
+
|
|
1795
|
+
# Show summary
|
|
1796
|
+
print("\nš New Configuration Summary:")
|
|
1797
|
+
print(
|
|
1798
|
+
f" System agents: {'Enabled' if settings.get('deploy_system_agents', True) else 'Disabled'}"
|
|
1799
|
+
)
|
|
1800
|
+
print(
|
|
1801
|
+
f" Local agents: {'Enabled' if settings.get('deploy_local_agents', True) else 'Disabled'}"
|
|
1802
|
+
)
|
|
1803
|
+
print(
|
|
1804
|
+
f" User agents: {'Enabled' if settings.get('deploy_user_agents', True) else 'Disabled'}"
|
|
1805
|
+
)
|
|
1806
|
+
|
|
1807
|
+
if settings.get("enabled_agents"):
|
|
1808
|
+
print(f" Enabled specific: {', '.join(settings['enabled_agents'])}")
|
|
1809
|
+
elif settings.get("disabled_agents"):
|
|
1810
|
+
print(f" Disabled specific: {', '.join(settings['disabled_agents'])}")
|
|
1811
|
+
else:
|
|
1812
|
+
print(" All agents enabled")
|
|
1813
|
+
|
|
1814
|
+
return CommandResult.success_result("Interactive configuration completed")
|
|
1815
|
+
|
|
1816
|
+
except KeyboardInterrupt:
|
|
1817
|
+
print("\n\nConfiguration cancelled.")
|
|
1818
|
+
return CommandResult.error_result("Configuration cancelled by user")
|
|
1819
|
+
except Exception as e:
|
|
1820
|
+
self.logger.error(f"Error in interactive configuration: {e}", exc_info=True)
|
|
1821
|
+
return CommandResult.error_result(
|
|
1822
|
+
f"Error in interactive configuration: {e}"
|
|
1823
|
+
)
|
|
1824
|
+
|
|
1825
|
+
def _migrate_to_project(self, args) -> CommandResult:
|
|
1826
|
+
"""Migrate user-level agents to project-level.
|
|
1827
|
+
|
|
1828
|
+
DEPRECATION: User-level agents (~/.claude-mpm/agents/) are deprecated and
|
|
1829
|
+
will be removed in v5.0.0. This command migrates them to project-level
|
|
1830
|
+
(.claude-mpm/agents/) where they belong.
|
|
1831
|
+
|
|
1832
|
+
Args:
|
|
1833
|
+
args: Command arguments with dry_run and force flags
|
|
1834
|
+
|
|
1835
|
+
Returns:
|
|
1836
|
+
CommandResult with migration status
|
|
1837
|
+
"""
|
|
1838
|
+
import shutil
|
|
1839
|
+
|
|
1840
|
+
try:
|
|
1841
|
+
user_agents_dir = Path.home() / ".claude-mpm" / "agents"
|
|
1842
|
+
project_agents_dir = Path.cwd() / ".claude-mpm" / "agents"
|
|
1843
|
+
|
|
1844
|
+
dry_run = getattr(args, "dry_run", False)
|
|
1845
|
+
force = getattr(args, "force", False)
|
|
1846
|
+
|
|
1847
|
+
# Check if user agents directory exists
|
|
1848
|
+
if not user_agents_dir.exists():
|
|
1849
|
+
print("ā
No user-level agents found. Nothing to migrate.")
|
|
1850
|
+
return CommandResult.success_result("No user-level agents to migrate")
|
|
1851
|
+
|
|
1852
|
+
# Find all user agent files
|
|
1853
|
+
user_agent_files = list(user_agents_dir.glob("*.json")) + list(
|
|
1854
|
+
user_agents_dir.glob("*.md")
|
|
1855
|
+
)
|
|
1856
|
+
|
|
1857
|
+
if not user_agent_files:
|
|
1858
|
+
print("ā
No user-level agents found. Nothing to migrate.")
|
|
1859
|
+
return CommandResult.success_result("No user-level agents to migrate")
|
|
1860
|
+
|
|
1861
|
+
# Display what we found
|
|
1862
|
+
print(f"\nš¦ Found {len(user_agent_files)} user-level agent(s) to migrate:")
|
|
1863
|
+
for agent_file in user_agent_files:
|
|
1864
|
+
print(f" - {agent_file.name}")
|
|
1865
|
+
|
|
1866
|
+
if dry_run:
|
|
1867
|
+
print("\nš DRY RUN: Would migrate to:")
|
|
1868
|
+
print(f" ā {project_agents_dir}")
|
|
1869
|
+
print("\nRun without --dry-run to perform the migration.")
|
|
1870
|
+
return CommandResult.success_result(
|
|
1871
|
+
"Dry run completed",
|
|
1872
|
+
data={
|
|
1873
|
+
"user_agents_found": len(user_agent_files),
|
|
1874
|
+
"target_directory": str(project_agents_dir),
|
|
1875
|
+
},
|
|
1876
|
+
)
|
|
1877
|
+
|
|
1878
|
+
# Create project agents directory
|
|
1879
|
+
project_agents_dir.mkdir(parents=True, exist_ok=True)
|
|
1880
|
+
|
|
1881
|
+
# Migrate agents
|
|
1882
|
+
migrated = 0
|
|
1883
|
+
skipped = 0
|
|
1884
|
+
errors = []
|
|
1885
|
+
|
|
1886
|
+
for agent_file in user_agent_files:
|
|
1887
|
+
target_file = project_agents_dir / agent_file.name
|
|
1888
|
+
|
|
1889
|
+
# Check for conflicts
|
|
1890
|
+
if target_file.exists() and not force:
|
|
1891
|
+
print(f"ā ļø Skipping {agent_file.name} (already exists in project)")
|
|
1892
|
+
print(" Use --force to overwrite existing agents")
|
|
1893
|
+
skipped += 1
|
|
1894
|
+
continue
|
|
1895
|
+
|
|
1896
|
+
try:
|
|
1897
|
+
# Copy agent to project directory
|
|
1898
|
+
shutil.copy2(agent_file, target_file)
|
|
1899
|
+
migrated += 1
|
|
1900
|
+
print(f"ā
Migrated {agent_file.name}")
|
|
1901
|
+
except Exception as e:
|
|
1902
|
+
error_msg = f"Failed to migrate {agent_file.name}: {e}"
|
|
1903
|
+
errors.append(error_msg)
|
|
1904
|
+
print(f"ā {error_msg}")
|
|
1905
|
+
|
|
1906
|
+
# Summary
|
|
1907
|
+
print("\nš Migration Summary:")
|
|
1908
|
+
print(f" ā
Migrated: {migrated}/{len(user_agent_files)}")
|
|
1909
|
+
if skipped > 0:
|
|
1910
|
+
print(f" āļø Skipped: {skipped} (already exist)")
|
|
1911
|
+
if errors:
|
|
1912
|
+
print(f" ā Errors: {len(errors)}")
|
|
1913
|
+
|
|
1914
|
+
if migrated > 0:
|
|
1915
|
+
print(f"\nā
Successfully migrated {migrated} agent(s) to:")
|
|
1916
|
+
print(f" {project_agents_dir}")
|
|
1917
|
+
print(
|
|
1918
|
+
"\nā ļø IMPORTANT: Verify agents work correctly, then remove user-level agents:"
|
|
1919
|
+
)
|
|
1920
|
+
print(f" rm -rf {user_agents_dir}")
|
|
1921
|
+
print("\nš” Why this change?")
|
|
1922
|
+
print(" - Project isolation: Each project has its own agents")
|
|
1923
|
+
print(" - Version control: Agents can be versioned with your code")
|
|
1924
|
+
print(" - Team consistency: Everyone uses the same agents")
|
|
1925
|
+
|
|
1926
|
+
return CommandResult.success_result(
|
|
1927
|
+
f"Migrated {migrated} agents",
|
|
1928
|
+
data={
|
|
1929
|
+
"migrated": migrated,
|
|
1930
|
+
"skipped": skipped,
|
|
1931
|
+
"errors": errors,
|
|
1932
|
+
"total": len(user_agent_files),
|
|
1933
|
+
},
|
|
1934
|
+
)
|
|
1935
|
+
|
|
1936
|
+
except Exception as e:
|
|
1937
|
+
self.logger.error(f"Error migrating agents: {e}", exc_info=True)
|
|
1938
|
+
return CommandResult.error_result(f"Error migrating agents: {e}")
|
|
1939
|
+
|
|
1940
|
+
def _deploy_minimal_configuration(self, args) -> CommandResult:
|
|
1941
|
+
"""Deploy minimal configuration (6 core agents).
|
|
1942
|
+
|
|
1943
|
+
Part of Phase 3 (1M-382): Agent Selection Modes.
|
|
1944
|
+
Deploy exactly 6 agents for basic Claude MPM workflow:
|
|
1945
|
+
engineer, documentation, qa, research, ops, ticketing.
|
|
1946
|
+
"""
|
|
1947
|
+
try:
|
|
1948
|
+
from ...config.agent_sources import AgentSourceConfiguration
|
|
1949
|
+
from ...services.agents.agent_selection_service import AgentSelectionService
|
|
1950
|
+
from ...services.agents.single_tier_deployment_service import (
|
|
1951
|
+
SingleTierDeploymentService,
|
|
1952
|
+
)
|
|
1953
|
+
|
|
1954
|
+
# Initialize services
|
|
1955
|
+
config = AgentSourceConfiguration.load()
|
|
1956
|
+
deployment_dir = Path.home() / ".claude" / "agents"
|
|
1957
|
+
deployment_service = SingleTierDeploymentService(config, deployment_dir)
|
|
1958
|
+
selection_service = AgentSelectionService(deployment_service)
|
|
1959
|
+
|
|
1960
|
+
# Get dry_run flag
|
|
1961
|
+
dry_run = getattr(args, "dry_run", False)
|
|
1962
|
+
|
|
1963
|
+
# Deploy minimal configuration
|
|
1964
|
+
print("šÆ Deploying minimal configuration (6 core agents)...")
|
|
1965
|
+
if dry_run:
|
|
1966
|
+
print("š DRY RUN MODE - No agents will be deployed\n")
|
|
1967
|
+
|
|
1968
|
+
result = selection_service.deploy_minimal_configuration(dry_run=dry_run)
|
|
1969
|
+
|
|
1970
|
+
# Format output
|
|
1971
|
+
output_format = self._get_output_format(args)
|
|
1972
|
+
if self._is_structured_format(output_format):
|
|
1973
|
+
formatted = (
|
|
1974
|
+
self._formatter.format_as_json(result)
|
|
1975
|
+
if str(output_format).lower() == OutputFormat.JSON
|
|
1976
|
+
else self._formatter.format_as_yaml(result)
|
|
1977
|
+
)
|
|
1978
|
+
print(formatted)
|
|
1979
|
+
return CommandResult.success_result(
|
|
1980
|
+
f"Minimal configuration {result['status']}", data=result
|
|
1981
|
+
)
|
|
1982
|
+
|
|
1983
|
+
# Text output
|
|
1984
|
+
print(f"\n{'=' * 60}")
|
|
1985
|
+
print(f"Status: {result['status'].upper()}")
|
|
1986
|
+
print(f"Mode: {result['mode']}")
|
|
1987
|
+
print(f"{'=' * 60}")
|
|
1988
|
+
print(
|
|
1989
|
+
f"\nš Summary: {result['deployed_count']} deployed, "
|
|
1990
|
+
f"{result['failed_count']} failed, {result['missing_count']} missing"
|
|
1991
|
+
)
|
|
1992
|
+
|
|
1993
|
+
if result["deployed_agents"]:
|
|
1994
|
+
print(f"\nā
Deployed agents ({len(result['deployed_agents'])}):")
|
|
1995
|
+
for agent in result["deployed_agents"]:
|
|
1996
|
+
print(f" ⢠{agent}")
|
|
1997
|
+
|
|
1998
|
+
if result["failed_agents"]:
|
|
1999
|
+
print(f"\nā Failed agents ({len(result['failed_agents'])}):")
|
|
2000
|
+
for agent in result["failed_agents"]:
|
|
2001
|
+
print(f" ⢠{agent}")
|
|
2002
|
+
|
|
2003
|
+
if result["missing_agents"]:
|
|
2004
|
+
print(f"\nā ļø Missing agents ({len(result['missing_agents'])}):")
|
|
2005
|
+
for agent in result["missing_agents"]:
|
|
2006
|
+
print(f" ⢠{agent}")
|
|
2007
|
+
print("\nThese agents are not available in configured sources.")
|
|
2008
|
+
|
|
2009
|
+
if dry_run:
|
|
2010
|
+
print(
|
|
2011
|
+
"\nš” This was a dry run. Run without --dry-run to deploy agents."
|
|
2012
|
+
)
|
|
2013
|
+
|
|
2014
|
+
return CommandResult.success_result(
|
|
2015
|
+
f"Minimal configuration {result['status']}", data=result
|
|
2016
|
+
)
|
|
2017
|
+
|
|
2018
|
+
except Exception as e:
|
|
2019
|
+
self.logger.error(
|
|
2020
|
+
f"Error deploying minimal configuration: {e}", exc_info=True
|
|
2021
|
+
)
|
|
2022
|
+
return CommandResult.error_result(
|
|
2023
|
+
f"Error deploying minimal configuration: {e}"
|
|
2024
|
+
)
|
|
2025
|
+
|
|
2026
|
+
def _deploy_auto_configure(self, args) -> CommandResult:
|
|
2027
|
+
"""Auto-detect toolchain and deploy matching agents.
|
|
2028
|
+
|
|
2029
|
+
Part of Phase 3 (1M-382): Agent Selection Modes.
|
|
2030
|
+
Detect project toolchain (languages, frameworks, build tools) and
|
|
2031
|
+
deploy matching specialized agents.
|
|
2032
|
+
"""
|
|
2033
|
+
try:
|
|
2034
|
+
from ...config.agent_sources import AgentSourceConfiguration
|
|
2035
|
+
from ...services.agents.agent_selection_service import AgentSelectionService
|
|
2036
|
+
from ...services.agents.single_tier_deployment_service import (
|
|
2037
|
+
SingleTierDeploymentService,
|
|
2038
|
+
)
|
|
2039
|
+
|
|
2040
|
+
# Initialize services
|
|
2041
|
+
config = AgentSourceConfiguration.load()
|
|
2042
|
+
deployment_dir = Path.home() / ".claude" / "agents"
|
|
2043
|
+
deployment_service = SingleTierDeploymentService(config, deployment_dir)
|
|
2044
|
+
selection_service = AgentSelectionService(deployment_service)
|
|
2045
|
+
|
|
2046
|
+
# Get arguments
|
|
2047
|
+
project_path = getattr(args, "path", Path.cwd())
|
|
2048
|
+
dry_run = getattr(args, "dry_run", False)
|
|
2049
|
+
|
|
2050
|
+
# Deploy auto-configure
|
|
2051
|
+
print(f"š Auto-detecting toolchain in {project_path}...")
|
|
2052
|
+
if dry_run:
|
|
2053
|
+
print("š DRY RUN MODE - No agents will be deployed\n")
|
|
2054
|
+
|
|
2055
|
+
result = selection_service.deploy_auto_configure(
|
|
2056
|
+
project_path=project_path, dry_run=dry_run
|
|
2057
|
+
)
|
|
2058
|
+
|
|
2059
|
+
# Format output
|
|
2060
|
+
output_format = self._get_output_format(args)
|
|
2061
|
+
if self._is_structured_format(output_format):
|
|
2062
|
+
formatted = (
|
|
2063
|
+
self._formatter.format_as_json(result)
|
|
2064
|
+
if str(output_format).lower() == OutputFormat.JSON
|
|
2065
|
+
else self._formatter.format_as_yaml(result)
|
|
2066
|
+
)
|
|
2067
|
+
print(formatted)
|
|
2068
|
+
return CommandResult.success_result(
|
|
2069
|
+
f"Auto-configure {result['status']}", data=result
|
|
2070
|
+
)
|
|
2071
|
+
|
|
2072
|
+
# Text output
|
|
2073
|
+
print(f"\n{'=' * 60}")
|
|
2074
|
+
print(f"Status: {result['status'].upper()}")
|
|
2075
|
+
print(f"Mode: {result['mode']}")
|
|
2076
|
+
print(f"{'=' * 60}")
|
|
2077
|
+
|
|
2078
|
+
# Show detected toolchain
|
|
2079
|
+
toolchain = result.get("toolchain", {})
|
|
2080
|
+
print("\nš§ Detected Toolchain:")
|
|
2081
|
+
if toolchain.get("languages"):
|
|
2082
|
+
print(f" Languages: {', '.join(toolchain['languages'])}")
|
|
2083
|
+
if toolchain.get("frameworks"):
|
|
2084
|
+
print(f" Frameworks: {', '.join(toolchain['frameworks'])}")
|
|
2085
|
+
if toolchain.get("build_tools"):
|
|
2086
|
+
print(f" Build Tools: {', '.join(toolchain['build_tools'])}")
|
|
2087
|
+
|
|
2088
|
+
if not any(toolchain.values()):
|
|
2089
|
+
print(" (No toolchain detected)")
|
|
2090
|
+
|
|
2091
|
+
# Show recommended agents
|
|
2092
|
+
recommended = result.get("recommended_agents", [])
|
|
2093
|
+
if recommended:
|
|
2094
|
+
print(f"\nšÆ Recommended agents ({len(recommended)}):")
|
|
2095
|
+
for agent in recommended:
|
|
2096
|
+
print(f" ⢠{agent}")
|
|
2097
|
+
|
|
2098
|
+
# Show deployment summary
|
|
2099
|
+
print(
|
|
2100
|
+
f"\nš Summary: {result['deployed_count']} deployed, "
|
|
2101
|
+
f"{result['failed_count']} failed, {result['missing_count']} missing"
|
|
2102
|
+
)
|
|
2103
|
+
|
|
2104
|
+
if result.get("deployed_agents"):
|
|
2105
|
+
print(f"\nā
Deployed agents ({len(result['deployed_agents'])}):")
|
|
2106
|
+
for agent in result["deployed_agents"]:
|
|
2107
|
+
print(f" ⢠{agent}")
|
|
2108
|
+
|
|
2109
|
+
if result.get("failed_agents"):
|
|
2110
|
+
print(f"\nā Failed agents ({len(result['failed_agents'])}):")
|
|
2111
|
+
for agent in result["failed_agents"]:
|
|
2112
|
+
print(f" ⢠{agent}")
|
|
2113
|
+
|
|
2114
|
+
if result.get("missing_agents"):
|
|
2115
|
+
print(f"\nā ļø Missing agents ({len(result['missing_agents'])}):")
|
|
2116
|
+
for agent in result["missing_agents"]:
|
|
2117
|
+
print(f" ⢠{agent}")
|
|
2118
|
+
print("\nThese agents are not available in configured sources.")
|
|
2119
|
+
|
|
2120
|
+
if dry_run:
|
|
2121
|
+
print(
|
|
2122
|
+
"\nš” This was a dry run. Run without --dry-run to deploy agents."
|
|
2123
|
+
)
|
|
2124
|
+
|
|
2125
|
+
return CommandResult.success_result(
|
|
2126
|
+
f"Auto-configure {result['status']}", data=result
|
|
2127
|
+
)
|
|
2128
|
+
|
|
2129
|
+
except Exception as e:
|
|
2130
|
+
self.logger.error(f"Error in auto-configure: {e}", exc_info=True)
|
|
2131
|
+
return CommandResult.error_result(f"Error in auto-configure: {e}")
|
|
2132
|
+
|
|
2133
|
+
def _list_collections(self, args) -> CommandResult:
|
|
2134
|
+
"""List all available agent collections.
|
|
2135
|
+
|
|
2136
|
+
NEW: Shows all collections with agent counts and metadata.
|
|
2137
|
+
Enables discovery of available agent collections before deployment.
|
|
2138
|
+
"""
|
|
2139
|
+
try:
|
|
2140
|
+
from pathlib import Path
|
|
2141
|
+
|
|
2142
|
+
from ...services.agents.deployment.remote_agent_discovery_service import (
|
|
2143
|
+
RemoteAgentDiscoveryService,
|
|
2144
|
+
)
|
|
2145
|
+
|
|
2146
|
+
# Get remote agents cache directory
|
|
2147
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
2148
|
+
|
|
2149
|
+
if not cache_dir.exists():
|
|
2150
|
+
return CommandResult.error_result(
|
|
2151
|
+
"No remote agent collections found. Run 'claude-mpm agents deploy' first."
|
|
2152
|
+
)
|
|
2153
|
+
|
|
2154
|
+
# Use RemoteAgentDiscoveryService to list collections
|
|
2155
|
+
remote_service = RemoteAgentDiscoveryService(cache_dir)
|
|
2156
|
+
collections = remote_service.list_collections()
|
|
2157
|
+
|
|
2158
|
+
if not collections:
|
|
2159
|
+
return CommandResult.success_result(
|
|
2160
|
+
"No agent collections found in cache.", data={"collections": []}
|
|
2161
|
+
)
|
|
2162
|
+
|
|
2163
|
+
# Format output
|
|
2164
|
+
output_lines = ["Available Agent Collections:\n"]
|
|
2165
|
+
for collection in collections:
|
|
2166
|
+
output_lines.append(
|
|
2167
|
+
f" ⢠{collection['collection_id']} ({collection['agent_count']} agents)"
|
|
2168
|
+
)
|
|
2169
|
+
|
|
2170
|
+
return CommandResult.success_result(
|
|
2171
|
+
"\n".join(output_lines), data={"collections": collections}
|
|
2172
|
+
)
|
|
2173
|
+
|
|
2174
|
+
except Exception as e:
|
|
2175
|
+
self.logger.error(f"Error listing collections: {e}", exc_info=True)
|
|
2176
|
+
return CommandResult.error_result(f"Error listing collections: {e}")
|
|
2177
|
+
|
|
2178
|
+
def _deploy_collection(self, args) -> CommandResult:
|
|
2179
|
+
"""Deploy all agents from a specific collection.
|
|
2180
|
+
|
|
2181
|
+
NEW: Enables bulk deployment of all agents from a named collection.
|
|
2182
|
+
Useful for deploying entire agent sets at once.
|
|
2183
|
+
"""
|
|
2184
|
+
try:
|
|
2185
|
+
from pathlib import Path
|
|
2186
|
+
|
|
2187
|
+
from ...services.agents.deployment.multi_source_deployment_service import (
|
|
2188
|
+
MultiSourceAgentDeploymentService,
|
|
2189
|
+
)
|
|
2190
|
+
|
|
2191
|
+
collection_id = args.collection_id
|
|
2192
|
+
|
|
2193
|
+
# Get agents from collection
|
|
2194
|
+
service = MultiSourceAgentDeploymentService()
|
|
2195
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
2196
|
+
agents = service.get_agents_by_collection(collection_id, cache_dir)
|
|
2197
|
+
|
|
2198
|
+
if not agents:
|
|
2199
|
+
return CommandResult.error_result(
|
|
2200
|
+
f"No agents found in collection '{collection_id}'"
|
|
2201
|
+
)
|
|
2202
|
+
|
|
2203
|
+
# Dry run mode
|
|
2204
|
+
if getattr(args, "dry_run", False):
|
|
2205
|
+
agent_names = [
|
|
2206
|
+
agent.get("metadata", {}).get("name", "Unknown") for agent in agents
|
|
2207
|
+
]
|
|
2208
|
+
output = f"Would deploy {len(agents)} agents from collection '{collection_id}':\n"
|
|
2209
|
+
for name in agent_names:
|
|
2210
|
+
output += f" ⢠{name}\n"
|
|
2211
|
+
return CommandResult.success_result(
|
|
2212
|
+
output,
|
|
2213
|
+
data={"collection_id": collection_id, "agent_count": len(agents)},
|
|
2214
|
+
)
|
|
2215
|
+
|
|
2216
|
+
# Deploy agents
|
|
2217
|
+
# TODO: Implement actual deployment logic using deployment service
|
|
2218
|
+
# For now, show what would be deployed
|
|
2219
|
+
return CommandResult.success_result(
|
|
2220
|
+
f"Deployment of collection '{collection_id}' would deploy {len(agents)} agents.\n"
|
|
2221
|
+
f"(Full deployment implementation pending)",
|
|
2222
|
+
data={
|
|
2223
|
+
"collection_id": collection_id,
|
|
2224
|
+
"agent_count": len(agents),
|
|
2225
|
+
"status": "pending_implementation",
|
|
2226
|
+
},
|
|
2227
|
+
)
|
|
2228
|
+
|
|
2229
|
+
except Exception as e:
|
|
2230
|
+
self.logger.error(f"Error deploying collection: {e}", exc_info=True)
|
|
2231
|
+
return CommandResult.error_result(f"Error deploying collection: {e}")
|
|
2232
|
+
|
|
2233
|
+
def _list_by_collection(self, args) -> CommandResult:
|
|
2234
|
+
"""List agents from a specific collection.
|
|
2235
|
+
|
|
2236
|
+
NEW: Shows detailed information about agents in a collection.
|
|
2237
|
+
Supports multiple output formats (table, json, yaml).
|
|
2238
|
+
"""
|
|
2239
|
+
try:
|
|
2240
|
+
import json as json_lib
|
|
2241
|
+
from pathlib import Path
|
|
2242
|
+
|
|
2243
|
+
from ...services.agents.deployment.multi_source_deployment_service import (
|
|
2244
|
+
MultiSourceAgentDeploymentService,
|
|
2245
|
+
)
|
|
2246
|
+
|
|
2247
|
+
collection_id = args.collection_id
|
|
2248
|
+
output_format = getattr(args, "format", "table")
|
|
2249
|
+
|
|
2250
|
+
# Get agents from collection
|
|
2251
|
+
service = MultiSourceAgentDeploymentService()
|
|
2252
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
2253
|
+
agents = service.get_agents_by_collection(collection_id, cache_dir)
|
|
2254
|
+
|
|
2255
|
+
if not agents:
|
|
2256
|
+
return CommandResult.error_result(
|
|
2257
|
+
f"No agents found in collection '{collection_id}'"
|
|
2258
|
+
)
|
|
2259
|
+
|
|
2260
|
+
# Format output based on requested format
|
|
2261
|
+
if output_format == "json":
|
|
2262
|
+
return CommandResult.success_result(
|
|
2263
|
+
json_lib.dumps(agents, indent=2),
|
|
2264
|
+
data={"collection_id": collection_id, "agents": agents},
|
|
2265
|
+
)
|
|
2266
|
+
if output_format == "yaml":
|
|
2267
|
+
try:
|
|
2268
|
+
import yaml
|
|
2269
|
+
|
|
2270
|
+
return CommandResult.success_result(
|
|
2271
|
+
yaml.dump(agents, default_flow_style=False),
|
|
2272
|
+
data={"collection_id": collection_id, "agents": agents},
|
|
2273
|
+
)
|
|
2274
|
+
except ImportError:
|
|
2275
|
+
return CommandResult.error_result(
|
|
2276
|
+
"YAML support not available (install PyYAML)"
|
|
2277
|
+
)
|
|
2278
|
+
|
|
2279
|
+
# Table format (default)
|
|
2280
|
+
output_lines = [f"Agents in collection '{collection_id}':\n"]
|
|
2281
|
+
for agent in agents:
|
|
2282
|
+
metadata = agent.get("metadata", {})
|
|
2283
|
+
name = metadata.get("name", "Unknown")
|
|
2284
|
+
description = metadata.get("description", "No description")
|
|
2285
|
+
version = agent.get("version", "unknown")
|
|
2286
|
+
output_lines.append(f" ⢠{name} (v{version})")
|
|
2287
|
+
output_lines.append(f" {description}\n")
|
|
2288
|
+
|
|
2289
|
+
return CommandResult.success_result(
|
|
2290
|
+
"\n".join(output_lines),
|
|
2291
|
+
data={"collection_id": collection_id, "agent_count": len(agents)},
|
|
2292
|
+
)
|
|
2293
|
+
|
|
2294
|
+
except Exception as e:
|
|
2295
|
+
self.logger.error(f"Error listing collection agents: {e}", exc_info=True)
|
|
2296
|
+
return CommandResult.error_result(f"Error listing collection agents: {e}")
|
|
2297
|
+
|
|
2298
|
+
def _cache_status(self, args) -> CommandResult:
|
|
2299
|
+
"""Show git status of agent cache.
|
|
2300
|
+
|
|
2301
|
+
Displays current branch, uncommitted changes, unpushed commits, and
|
|
2302
|
+
remote URL for the agent cache repository.
|
|
2303
|
+
"""
|
|
2304
|
+
try:
|
|
2305
|
+
from ...services.agents.cache_git_manager import CacheGitManager
|
|
2306
|
+
|
|
2307
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
2308
|
+
manager = CacheGitManager(cache_dir)
|
|
2309
|
+
|
|
2310
|
+
if not manager.is_git_repo():
|
|
2311
|
+
print("ā Cache is not a git repository")
|
|
2312
|
+
print(f"\nCache location: {cache_dir}")
|
|
2313
|
+
print(
|
|
2314
|
+
"\nš” This is expected if you haven't cloned the agents repository."
|
|
2315
|
+
)
|
|
2316
|
+
print(" The cache will be managed via HTTP sync instead.")
|
|
2317
|
+
return CommandResult.error_result("Cache is not a git repository")
|
|
2318
|
+
|
|
2319
|
+
status = manager.get_status()
|
|
2320
|
+
output_format = self._get_output_format(args)
|
|
2321
|
+
|
|
2322
|
+
if self._is_structured_format(output_format):
|
|
2323
|
+
formatted = (
|
|
2324
|
+
self._formatter.format_as_json(status)
|
|
2325
|
+
if str(output_format).lower() == OutputFormat.JSON
|
|
2326
|
+
else self._formatter.format_as_yaml(status)
|
|
2327
|
+
)
|
|
2328
|
+
print(formatted)
|
|
2329
|
+
return CommandResult.success_result(
|
|
2330
|
+
"Cache status retrieved", data=status
|
|
2331
|
+
)
|
|
2332
|
+
|
|
2333
|
+
# Text output
|
|
2334
|
+
print(f"\nš Cache: {manager.repo_path}")
|
|
2335
|
+
print(f"šæ Branch: {status.get('branch', 'unknown')}")
|
|
2336
|
+
|
|
2337
|
+
if status.get("remote_url"):
|
|
2338
|
+
print(f"š Remote: {status['remote_url']}")
|
|
2339
|
+
|
|
2340
|
+
# Show sync status
|
|
2341
|
+
ahead = status.get("ahead", 0)
|
|
2342
|
+
behind = status.get("behind", 0)
|
|
2343
|
+
|
|
2344
|
+
if ahead > 0:
|
|
2345
|
+
print(f"š¤ Ahead of remote: {ahead} commit(s)")
|
|
2346
|
+
if behind > 0:
|
|
2347
|
+
print(f"š„ Behind remote: {behind} commit(s)")
|
|
2348
|
+
|
|
2349
|
+
if ahead == 0 and behind == 0:
|
|
2350
|
+
print("ā
In sync with remote")
|
|
2351
|
+
|
|
2352
|
+
# Show uncommitted changes
|
|
2353
|
+
uncommitted = status.get("uncommitted", [])
|
|
2354
|
+
if uncommitted:
|
|
2355
|
+
print(f"\nā ļø Uncommitted changes: {len(uncommitted)}")
|
|
2356
|
+
for file in uncommitted[:10]: # Show max 10 files
|
|
2357
|
+
print(f" - {file}")
|
|
2358
|
+
if len(uncommitted) > 10:
|
|
2359
|
+
print(f" ... and {len(uncommitted) - 10} more")
|
|
2360
|
+
else:
|
|
2361
|
+
print("\nā
No uncommitted changes")
|
|
2362
|
+
|
|
2363
|
+
# Overall status
|
|
2364
|
+
if status.get("is_clean"):
|
|
2365
|
+
print("\n⨠Cache is clean and up-to-date")
|
|
2366
|
+
else:
|
|
2367
|
+
print("\nš” Run 'claude-mpm agents cache-sync' to sync with remote")
|
|
2368
|
+
|
|
2369
|
+
return CommandResult.success_result("Cache status displayed", data=status)
|
|
2370
|
+
|
|
2371
|
+
except Exception as e:
|
|
2372
|
+
self.logger.error(f"Error getting cache status: {e}", exc_info=True)
|
|
2373
|
+
return CommandResult.error_result(f"Error getting cache status: {e}")
|
|
2374
|
+
|
|
2375
|
+
def _cache_pull(self, args) -> CommandResult:
|
|
2376
|
+
"""Pull latest agents from remote repository."""
|
|
2377
|
+
try:
|
|
2378
|
+
from ...services.agents.cache_git_manager import CacheGitManager
|
|
2379
|
+
|
|
2380
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
2381
|
+
manager = CacheGitManager(cache_dir)
|
|
2382
|
+
|
|
2383
|
+
if not manager.is_git_repo():
|
|
2384
|
+
print("ā Cache is not a git repository")
|
|
2385
|
+
return CommandResult.error_result("Cache is not a git repository")
|
|
2386
|
+
|
|
2387
|
+
branch = getattr(args, "branch", "main")
|
|
2388
|
+
print(f"š Pulling latest changes from {branch}...")
|
|
2389
|
+
|
|
2390
|
+
success, msg = manager.pull_latest(branch)
|
|
2391
|
+
|
|
2392
|
+
if success:
|
|
2393
|
+
print(f"ā
{msg}")
|
|
2394
|
+
return CommandResult.success_result(msg)
|
|
2395
|
+
print(f"ā {msg}")
|
|
2396
|
+
return CommandResult.error_result(msg)
|
|
2397
|
+
|
|
2398
|
+
except Exception as e:
|
|
2399
|
+
self.logger.error(f"Error pulling cache: {e}", exc_info=True)
|
|
2400
|
+
return CommandResult.error_result(f"Error pulling cache: {e}")
|
|
2401
|
+
|
|
2402
|
+
def _cache_commit(self, args) -> CommandResult:
|
|
2403
|
+
"""Commit changes to cache repository."""
|
|
2404
|
+
try:
|
|
2405
|
+
from ...services.agents.cache_git_manager import CacheGitManager
|
|
2406
|
+
|
|
2407
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
2408
|
+
manager = CacheGitManager(cache_dir)
|
|
2409
|
+
|
|
2410
|
+
if not manager.is_git_repo():
|
|
2411
|
+
print("ā Cache is not a git repository")
|
|
2412
|
+
return CommandResult.error_result("Cache is not a git repository")
|
|
2413
|
+
|
|
2414
|
+
# Get commit message from args
|
|
2415
|
+
message = getattr(args, "message", None)
|
|
2416
|
+
if not message:
|
|
2417
|
+
# Default message
|
|
2418
|
+
message = "feat: update agents from local development"
|
|
2419
|
+
|
|
2420
|
+
print("š¾ Committing changes...")
|
|
2421
|
+
success, msg = manager.commit_changes(message)
|
|
2422
|
+
|
|
2423
|
+
if success:
|
|
2424
|
+
print(f"ā
{msg}")
|
|
2425
|
+
print(f"\nš” Commit message: {message}")
|
|
2426
|
+
return CommandResult.success_result(msg)
|
|
2427
|
+
print(f"ā {msg}")
|
|
2428
|
+
return CommandResult.error_result(msg)
|
|
2429
|
+
|
|
2430
|
+
except Exception as e:
|
|
2431
|
+
self.logger.error(f"Error committing cache changes: {e}", exc_info=True)
|
|
2432
|
+
return CommandResult.error_result(f"Error committing cache changes: {e}")
|
|
2433
|
+
|
|
2434
|
+
def _cache_push(self, args) -> CommandResult:
|
|
2435
|
+
"""Push local agent changes to remote."""
|
|
2436
|
+
try:
|
|
2437
|
+
from ...services.agents.cache_git_manager import CacheGitManager
|
|
2438
|
+
|
|
2439
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
2440
|
+
manager = CacheGitManager(cache_dir)
|
|
2441
|
+
|
|
2442
|
+
if not manager.is_git_repo():
|
|
2443
|
+
print("ā Cache is not a git repository")
|
|
2444
|
+
return CommandResult.error_result("Cache is not a git repository")
|
|
2445
|
+
|
|
2446
|
+
# Check for uncommitted changes
|
|
2447
|
+
if manager.has_uncommitted_changes():
|
|
2448
|
+
print("ā ļø You have uncommitted changes.")
|
|
2449
|
+
print("\nš” Commit changes first with:")
|
|
2450
|
+
print(" claude-mpm agents cache-commit --message 'your message'")
|
|
2451
|
+
|
|
2452
|
+
# Ask if user wants to commit first
|
|
2453
|
+
auto_commit = getattr(args, "auto_commit", False)
|
|
2454
|
+
if auto_commit:
|
|
2455
|
+
print("\nš Auto-committing changes...")
|
|
2456
|
+
success, msg = manager.commit_changes("feat: update agents")
|
|
2457
|
+
if not success:
|
|
2458
|
+
print(f"ā Commit failed: {msg}")
|
|
2459
|
+
return CommandResult.error_result(f"Commit failed: {msg}")
|
|
2460
|
+
print(f"ā
{msg}")
|
|
2461
|
+
else:
|
|
2462
|
+
return CommandResult.error_result(
|
|
2463
|
+
"Uncommitted changes detected. Commit first or use --auto-commit"
|
|
2464
|
+
)
|
|
2465
|
+
|
|
2466
|
+
# Push changes
|
|
2467
|
+
branch = getattr(args, "branch", "main")
|
|
2468
|
+
print(f"š¤ Pushing changes to {branch}...")
|
|
2469
|
+
|
|
2470
|
+
success, msg = manager.push_changes(branch)
|
|
2471
|
+
|
|
2472
|
+
if success:
|
|
2473
|
+
print(f"ā
{msg}")
|
|
2474
|
+
return CommandResult.success_result(msg)
|
|
2475
|
+
print(f"ā {msg}")
|
|
2476
|
+
return CommandResult.error_result(msg)
|
|
2477
|
+
|
|
2478
|
+
except Exception as e:
|
|
2479
|
+
self.logger.error(f"Error pushing cache: {e}", exc_info=True)
|
|
2480
|
+
return CommandResult.error_result(f"Error pushing cache: {e}")
|
|
2481
|
+
|
|
2482
|
+
def _cache_sync(self, args) -> CommandResult:
|
|
2483
|
+
"""Full cache sync: pull, commit (if needed), push."""
|
|
2484
|
+
try:
|
|
2485
|
+
from ...services.agents.cache_git_manager import CacheGitManager
|
|
2486
|
+
|
|
2487
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
2488
|
+
manager = CacheGitManager(cache_dir)
|
|
2489
|
+
|
|
2490
|
+
if not manager.is_git_repo():
|
|
2491
|
+
print("ā Cache is not a git repository")
|
|
2492
|
+
return CommandResult.error_result("Cache is not a git repository")
|
|
2493
|
+
|
|
2494
|
+
print("š Syncing cache with remote...\n")
|
|
2495
|
+
|
|
2496
|
+
success, msg = manager.sync_with_remote()
|
|
2497
|
+
|
|
2498
|
+
# Print detailed sync message
|
|
2499
|
+
print(msg)
|
|
2500
|
+
|
|
2501
|
+
if success:
|
|
2502
|
+
print("\n⨠Cache sync complete!")
|
|
2503
|
+
return CommandResult.success_result("Cache synced successfully")
|
|
2504
|
+
|
|
2505
|
+
print("\nā Cache sync failed. See details above.")
|
|
2506
|
+
return CommandResult.error_result("Cache sync failed")
|
|
2507
|
+
|
|
2508
|
+
except Exception as e:
|
|
2509
|
+
self.logger.error(f"Error syncing cache: {e}", exc_info=True)
|
|
2510
|
+
return CommandResult.error_result(f"Error syncing cache: {e}")
|
|
2511
|
+
|
|
2512
|
+
|
|
2513
|
+
def manage_agents(args):
|
|
168
2514
|
"""
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
their working directory.
|
|
173
|
-
|
|
174
|
-
Args:
|
|
175
|
-
args: Command arguments with optional 'target' path
|
|
176
|
-
deployment_service: Agent deployment service instance
|
|
2515
|
+
Main entry point for agent management commands.
|
|
2516
|
+
|
|
2517
|
+
This function maintains backward compatibility while using the new AgentCommand pattern.
|
|
177
2518
|
"""
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
print("No system agents found to remove")
|
|
187
|
-
|
|
188
|
-
if results["errors"]:
|
|
189
|
-
print("\nā Errors during cleanup:")
|
|
190
|
-
for error in results["errors"]:
|
|
191
|
-
print(f" - {error}")
|
|
2519
|
+
command = AgentsCommand()
|
|
2520
|
+
result = command.execute(args)
|
|
2521
|
+
|
|
2522
|
+
# Print result if structured output format is requested
|
|
2523
|
+
if _is_structured_output(args):
|
|
2524
|
+
command.print_result(result, args)
|
|
2525
|
+
|
|
2526
|
+
return result.exit_code
|