claude-mpm 4.13.2__py3-none-any.whl ā 4.18.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +48 -17
- claude_mpm/agents/OUTPUT_STYLE.md +329 -11
- claude_mpm/agents/PM_INSTRUCTIONS.md +227 -8
- claude_mpm/agents/agent_loader.py +17 -5
- claude_mpm/agents/frontmatter_validator.py +284 -253
- claude_mpm/agents/templates/agentic-coder-optimizer.json +9 -2
- claude_mpm/agents/templates/api_qa.json +7 -1
- claude_mpm/agents/templates/clerk-ops.json +8 -1
- claude_mpm/agents/templates/code_analyzer.json +4 -1
- claude_mpm/agents/templates/dart_engineer.json +11 -1
- claude_mpm/agents/templates/data_engineer.json +11 -1
- claude_mpm/agents/templates/documentation.json +6 -1
- claude_mpm/agents/templates/engineer.json +18 -1
- claude_mpm/agents/templates/gcp_ops_agent.json +8 -1
- claude_mpm/agents/templates/golang_engineer.json +11 -1
- claude_mpm/agents/templates/java_engineer.json +12 -2
- claude_mpm/agents/templates/local_ops_agent.json +1217 -6
- claude_mpm/agents/templates/nextjs_engineer.json +11 -1
- claude_mpm/agents/templates/ops.json +8 -1
- claude_mpm/agents/templates/php-engineer.json +11 -1
- claude_mpm/agents/templates/project_organizer.json +10 -3
- claude_mpm/agents/templates/prompt-engineer.json +5 -1
- claude_mpm/agents/templates/python_engineer.json +11 -1
- claude_mpm/agents/templates/qa.json +7 -1
- claude_mpm/agents/templates/react_engineer.json +11 -1
- claude_mpm/agents/templates/refactoring_engineer.json +8 -1
- claude_mpm/agents/templates/research.json +4 -1
- claude_mpm/agents/templates/ruby-engineer.json +11 -1
- claude_mpm/agents/templates/rust_engineer.json +11 -1
- claude_mpm/agents/templates/security.json +6 -1
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/agents/templates/ticketing.json +6 -1
- claude_mpm/agents/templates/typescript_engineer.json +11 -1
- claude_mpm/agents/templates/vercel_ops_agent.json +8 -1
- claude_mpm/agents/templates/version_control.json +8 -1
- claude_mpm/agents/templates/web_qa.json +7 -1
- claude_mpm/agents/templates/web_ui.json +11 -1
- claude_mpm/cli/__init__.py +34 -706
- claude_mpm/cli/commands/agent_manager.py +25 -12
- claude_mpm/cli/commands/agent_state_manager.py +186 -0
- claude_mpm/cli/commands/agents.py +204 -148
- claude_mpm/cli/commands/aggregate.py +7 -3
- claude_mpm/cli/commands/analyze.py +9 -4
- claude_mpm/cli/commands/analyze_code.py +7 -2
- claude_mpm/cli/commands/auto_configure.py +7 -9
- claude_mpm/cli/commands/config.py +47 -13
- claude_mpm/cli/commands/configure.py +294 -1788
- claude_mpm/cli/commands/configure_agent_display.py +261 -0
- claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
- claude_mpm/cli/commands/configure_hook_manager.py +225 -0
- claude_mpm/cli/commands/configure_models.py +18 -0
- claude_mpm/cli/commands/configure_navigation.py +167 -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/local_deploy.py +537 -0
- claude_mpm/cli/commands/memory.py +54 -20
- claude_mpm/cli/commands/mpm_init.py +39 -25
- claude_mpm/cli/commands/mpm_init_handler.py +8 -3
- claude_mpm/cli/executor.py +202 -0
- claude_mpm/cli/helpers.py +105 -0
- claude_mpm/cli/interactive/__init__.py +3 -0
- claude_mpm/cli/interactive/skills_wizard.py +491 -0
- claude_mpm/cli/parsers/__init__.py +7 -1
- claude_mpm/cli/parsers/base_parser.py +98 -3
- claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
- claude_mpm/cli/shared/output_formatters.py +28 -19
- claude_mpm/cli/startup.py +481 -0
- claude_mpm/cli/utils.py +52 -1
- claude_mpm/commands/mpm-help.md +3 -0
- claude_mpm/commands/mpm-version.md +113 -0
- claude_mpm/commands/mpm.md +1 -0
- claude_mpm/config/agent_config.py +2 -2
- claude_mpm/config/model_config.py +428 -0
- claude_mpm/core/base_service.py +13 -12
- claude_mpm/core/enums.py +452 -0
- claude_mpm/core/factories.py +1 -1
- claude_mpm/core/instruction_reinforcement_hook.py +2 -1
- claude_mpm/core/interactive_session.py +9 -3
- claude_mpm/core/logging_config.py +6 -2
- claude_mpm/core/oneshot_session.py +8 -4
- claude_mpm/core/optimized_agent_loader.py +3 -3
- claude_mpm/core/output_style_manager.py +12 -192
- claude_mpm/core/service_registry.py +5 -1
- claude_mpm/core/types.py +2 -9
- claude_mpm/core/typing_utils.py +7 -6
- claude_mpm/dashboard/static/js/dashboard.js +0 -14
- claude_mpm/dashboard/templates/index.html +3 -41
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/hooks/instruction_reinforcement.py +7 -2
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/auto_config_manager.py +10 -11
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
- claude_mpm/services/agents/deployment/agent_validator.py +17 -1
- claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
- claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
- claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
- claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
- claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
- claude_mpm/services/agents/local_template_manager.py +1 -1
- claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
- claude_mpm/services/agents/registry/modification_tracker.py +5 -2
- claude_mpm/services/command_handler_service.py +11 -5
- claude_mpm/services/core/interfaces/__init__.py +74 -2
- claude_mpm/services/core/interfaces/health.py +172 -0
- claude_mpm/services/core/interfaces/model.py +281 -0
- claude_mpm/services/core/interfaces/process.py +372 -0
- claude_mpm/services/core/interfaces/restart.py +307 -0
- claude_mpm/services/core/interfaces/stability.py +260 -0
- claude_mpm/services/core/models/__init__.py +33 -0
- claude_mpm/services/core/models/agent_config.py +12 -28
- claude_mpm/services/core/models/health.py +162 -0
- claude_mpm/services/core/models/process.py +235 -0
- claude_mpm/services/core/models/restart.py +302 -0
- claude_mpm/services/core/models/stability.py +264 -0
- claude_mpm/services/core/path_resolver.py +23 -7
- claude_mpm/services/diagnostics/__init__.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
- claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
- claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
- claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
- claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
- claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
- claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +36 -31
- claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
- claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
- claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
- claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
- claude_mpm/services/diagnostics/models.py +19 -24
- claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
- claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
- claude_mpm/services/infrastructure/monitoring/base.py +5 -13
- claude_mpm/services/infrastructure/monitoring/network.py +7 -6
- claude_mpm/services/infrastructure/monitoring/process.py +13 -12
- claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
- claude_mpm/services/infrastructure/monitoring/service.py +16 -15
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/local_ops/__init__.py +163 -0
- claude_mpm/services/local_ops/crash_detector.py +257 -0
- claude_mpm/services/local_ops/health_checks/__init__.py +28 -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 +430 -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 +9 -4
- claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
- claude_mpm/services/mcp_gateway/core/base.py +18 -31
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +71 -24
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
- claude_mpm/services/memory_hook_service.py +4 -1
- 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 +453 -0
- claude_mpm/services/model/ollama_provider.py +415 -0
- claude_mpm/services/monitor/daemon_manager.py +3 -2
- claude_mpm/services/monitor/handlers/dashboard.py +2 -1
- claude_mpm/services/monitor/handlers/hooks.py +2 -1
- claude_mpm/services/monitor/management/lifecycle.py +3 -2
- claude_mpm/services/monitor/server.py +2 -1
- claude_mpm/services/session_management_service.py +3 -2
- claude_mpm/services/session_manager.py +205 -1
- claude_mpm/services/shared/async_service_base.py +16 -27
- claude_mpm/services/shared/lifecycle_service_base.py +1 -14
- claude_mpm/services/socketio/handlers/__init__.py +5 -2
- claude_mpm/services/socketio/handlers/hook.py +13 -2
- claude_mpm/services/socketio/handlers/registry.py +4 -2
- claude_mpm/services/socketio/server/main.py +10 -8
- claude_mpm/services/subprocess_launcher_service.py +14 -5
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
- claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
- claude_mpm/services/unified/deployment_strategies/local.py +6 -5
- claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
- claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
- claude_mpm/services/unified/interfaces.py +3 -1
- claude_mpm/services/unified/unified_analyzer.py +14 -10
- claude_mpm/services/unified/unified_config.py +2 -1
- claude_mpm/services/unified/unified_deployment.py +9 -4
- claude_mpm/services/version_service.py +104 -1
- claude_mpm/skills/__init__.py +21 -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/json-data-handling.md +223 -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 +567 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/security-scanning.md +327 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/skills/registry.py +286 -0
- claude_mpm/skills/skill_manager.py +310 -0
- claude_mpm/tools/code_tree_analyzer.py +177 -141
- claude_mpm/tools/code_tree_events.py +4 -2
- claude_mpm/utils/agent_dependency_loader.py +2 -2
- {claude_mpm-4.13.2.dist-info ā claude_mpm-4.18.2.dist-info}/METADATA +117 -8
- {claude_mpm-4.13.2.dist-info ā claude_mpm-4.18.2.dist-info}/RECORD +238 -174
- claude_mpm/dashboard/static/css/code-tree.css +0 -1639
- claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
- claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
- claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
- claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
- claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
- claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
- claude_mpm/services/project/analyzer_refactored.py +0 -450
- {claude_mpm-4.13.2.dist-info ā claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.13.2.dist-info ā claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.13.2.dist-info ā claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.13.2.dist-info ā claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI Helper Utilities
|
|
3
|
+
====================
|
|
4
|
+
|
|
5
|
+
This module provides helper functions for the CLI, including configuration
|
|
6
|
+
checks and user prompts.
|
|
7
|
+
|
|
8
|
+
Part of cli/__init__.py refactoring to reduce file size and improve modularity.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def has_configuration_file() -> bool:
|
|
16
|
+
"""Check if any configuration file exists in standard locations."""
|
|
17
|
+
config_paths = [
|
|
18
|
+
Path.cwd() / ".claude-mpm" / "configuration.yaml",
|
|
19
|
+
Path.cwd() / ".claude-mpm" / "configuration.yml",
|
|
20
|
+
Path.home() / ".claude-mpm" / "configuration.yaml",
|
|
21
|
+
Path.home() / ".claude-mpm" / "configuration.yml",
|
|
22
|
+
]
|
|
23
|
+
return any(path.exists() for path in config_paths)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def is_interactive_session() -> bool:
|
|
27
|
+
"""Check if running in an interactive terminal."""
|
|
28
|
+
return sys.stdin.isatty() and sys.stdout.isatty()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def should_skip_config_check(command: str | None) -> bool:
|
|
32
|
+
"""Check if command should skip configuration check."""
|
|
33
|
+
skip_commands = ["configure", "doctor", "info", "mcp", "config"]
|
|
34
|
+
return command in skip_commands if command else False
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def prompt_for_configuration() -> bool:
|
|
38
|
+
"""Prompt user to run configuration wizard.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
bool: True if user wants to configure, False otherwise
|
|
42
|
+
"""
|
|
43
|
+
from rich.console import Console as RichConsole
|
|
44
|
+
from rich.prompt import Confirm
|
|
45
|
+
|
|
46
|
+
console = RichConsole()
|
|
47
|
+
|
|
48
|
+
console.print()
|
|
49
|
+
console.print("[yellow]āļø Configuration Recommended[/yellow]")
|
|
50
|
+
console.print()
|
|
51
|
+
console.print("Claude MPM works best with proper configuration.")
|
|
52
|
+
console.print("The configurator helps you:")
|
|
53
|
+
console.print(" ⢠Enable/disable MCP services (ticketer, browser, vector-search)")
|
|
54
|
+
console.print(" ⢠Configure hook services (monitor, dashboard)")
|
|
55
|
+
console.print(" ⢠Select system agents to use")
|
|
56
|
+
console.print()
|
|
57
|
+
|
|
58
|
+
return Confirm.ask("Would you like to run the configurator now?", default=True)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def handle_missing_configuration():
|
|
62
|
+
"""Handle missing configuration file by prompting user to configure.
|
|
63
|
+
|
|
64
|
+
WHY: Centralizes the logic for handling missing configuration files,
|
|
65
|
+
keeping main() cleaner and more focused.
|
|
66
|
+
"""
|
|
67
|
+
if is_interactive_session():
|
|
68
|
+
# Interactive: Offer to run configurator
|
|
69
|
+
if prompt_for_configuration():
|
|
70
|
+
from rich.console import Console as RichConsole
|
|
71
|
+
|
|
72
|
+
# Import configure command only when needed
|
|
73
|
+
from .commands.configure import ConfigureCommand
|
|
74
|
+
|
|
75
|
+
console = RichConsole()
|
|
76
|
+
config_cmd = ConfigureCommand()
|
|
77
|
+
try:
|
|
78
|
+
config_cmd.execute({})
|
|
79
|
+
console.print(
|
|
80
|
+
"\n[green]ā[/green] Configuration complete! "
|
|
81
|
+
"Continuing with command...\n"
|
|
82
|
+
)
|
|
83
|
+
except Exception as e:
|
|
84
|
+
console.print(f"\n[yellow]ā [/yellow] Configuration error: {e}")
|
|
85
|
+
console.print(
|
|
86
|
+
"[yellow]Continuing with default configuration...\n[/yellow]"
|
|
87
|
+
)
|
|
88
|
+
else:
|
|
89
|
+
from rich.console import Console as RichConsole
|
|
90
|
+
|
|
91
|
+
console = RichConsole()
|
|
92
|
+
console.print("\n[dim]Using default configuration.[/dim]")
|
|
93
|
+
console.print(
|
|
94
|
+
"[dim]Run 'claude-mpm configure' anytime to set up your "
|
|
95
|
+
"preferences.\n[/dim]"
|
|
96
|
+
)
|
|
97
|
+
else:
|
|
98
|
+
# Non-interactive: Log message only
|
|
99
|
+
import logging
|
|
100
|
+
|
|
101
|
+
logger = logging.getLogger(__name__)
|
|
102
|
+
logger.info(
|
|
103
|
+
"Configuration file not found, using defaults. "
|
|
104
|
+
"Run 'claude-mpm configure' to customize."
|
|
105
|
+
)
|
|
@@ -10,9 +10,12 @@ from .agent_wizard import (
|
|
|
10
10
|
run_interactive_agent_manager,
|
|
11
11
|
run_interactive_agent_wizard,
|
|
12
12
|
)
|
|
13
|
+
from .skills_wizard import SkillsWizard, discover_and_link_runtime_skills
|
|
13
14
|
|
|
14
15
|
__all__ = [
|
|
15
16
|
"AgentWizard",
|
|
17
|
+
"SkillsWizard",
|
|
18
|
+
"discover_and_link_runtime_skills",
|
|
16
19
|
"run_interactive_agent_manager",
|
|
17
20
|
"run_interactive_agent_wizard",
|
|
18
21
|
]
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
"""Interactive Skills Selection Wizard for Claude MPM.
|
|
2
|
+
|
|
3
|
+
This module provides a step-by-step interactive wizard for selecting and configuring
|
|
4
|
+
skills for agents with user-friendly prompts and intelligent auto-linking.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, List, Optional, Tuple
|
|
8
|
+
|
|
9
|
+
from claude_mpm.core.logging_config import get_logger
|
|
10
|
+
from claude_mpm.skills.registry import get_registry
|
|
11
|
+
from claude_mpm.skills.skill_manager import get_manager
|
|
12
|
+
|
|
13
|
+
logger = get_logger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Agent-to-skills auto-linking mappings
|
|
17
|
+
ENGINEER_CORE_SKILLS = [
|
|
18
|
+
"test-driven-development",
|
|
19
|
+
"systematic-debugging",
|
|
20
|
+
"code-review",
|
|
21
|
+
"refactoring-patterns",
|
|
22
|
+
"git-workflow",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
PYTHON_SKILLS = ENGINEER_CORE_SKILLS + ["async-testing"]
|
|
26
|
+
TYPESCRIPT_SKILLS = ENGINEER_CORE_SKILLS + ["async-testing"]
|
|
27
|
+
GOLANG_SKILLS = ENGINEER_CORE_SKILLS + ["async-testing"]
|
|
28
|
+
REACT_SKILLS = TYPESCRIPT_SKILLS + ["performance-profiling"]
|
|
29
|
+
NEXTJS_SKILLS = REACT_SKILLS
|
|
30
|
+
VUE_SKILLS = TYPESCRIPT_SKILLS + ["performance-profiling"]
|
|
31
|
+
|
|
32
|
+
OPS_SKILLS = [
|
|
33
|
+
"docker-containerization",
|
|
34
|
+
"database-migration",
|
|
35
|
+
"security-scanning",
|
|
36
|
+
"systematic-debugging",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
DOCUMENTATION_SKILLS = [
|
|
40
|
+
"api-documentation",
|
|
41
|
+
"code-review",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
QA_SKILLS = [
|
|
45
|
+
"test-driven-development",
|
|
46
|
+
"systematic-debugging",
|
|
47
|
+
"async-testing",
|
|
48
|
+
"performance-profiling",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
# Mapping of agent types to their recommended skills
|
|
52
|
+
AGENT_SKILL_MAPPING = {
|
|
53
|
+
# Engineer agents
|
|
54
|
+
"engineer": ENGINEER_CORE_SKILLS,
|
|
55
|
+
"python-engineer": PYTHON_SKILLS,
|
|
56
|
+
"typescript-engineer": TYPESCRIPT_SKILLS,
|
|
57
|
+
"golang-engineer": GOLANG_SKILLS,
|
|
58
|
+
"react-engineer": REACT_SKILLS,
|
|
59
|
+
"nextjs-engineer": NEXTJS_SKILLS,
|
|
60
|
+
"vue-engineer": VUE_SKILLS,
|
|
61
|
+
# Ops agents
|
|
62
|
+
"ops": OPS_SKILLS,
|
|
63
|
+
"devops": OPS_SKILLS,
|
|
64
|
+
"local-ops": OPS_SKILLS,
|
|
65
|
+
# Documentation agents
|
|
66
|
+
"docs": DOCUMENTATION_SKILLS,
|
|
67
|
+
"documentation": DOCUMENTATION_SKILLS,
|
|
68
|
+
"technical-writer": DOCUMENTATION_SKILLS,
|
|
69
|
+
# QA agents
|
|
70
|
+
"qa": QA_SKILLS,
|
|
71
|
+
"web-qa": QA_SKILLS,
|
|
72
|
+
"api-qa": QA_SKILLS,
|
|
73
|
+
"tester": QA_SKILLS,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class SkillsWizard:
|
|
78
|
+
"""Interactive wizard for skills selection and configuration."""
|
|
79
|
+
|
|
80
|
+
def __init__(self):
|
|
81
|
+
"""Initialize the skills wizard."""
|
|
82
|
+
self.registry = get_registry()
|
|
83
|
+
self.manager = get_manager()
|
|
84
|
+
self.logger = logger
|
|
85
|
+
|
|
86
|
+
def run_interactive_selection(
|
|
87
|
+
self, selected_agents: Optional[List[str]] = None
|
|
88
|
+
) -> Tuple[bool, Dict[str, List[str]]]:
|
|
89
|
+
"""Run interactive skills selection wizard.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
selected_agents: List of agent IDs that were selected in agent wizard
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Tuple of (success, agent_skills_mapping)
|
|
96
|
+
- success: Boolean indicating if selection was successful
|
|
97
|
+
- agent_skills_mapping: Dict mapping agent IDs to lists of skill names
|
|
98
|
+
"""
|
|
99
|
+
try:
|
|
100
|
+
print("\n" + "=" * 60)
|
|
101
|
+
print("šÆ Skills Selection Wizard")
|
|
102
|
+
print("=" * 60)
|
|
103
|
+
print("\nI'll help you select skills for your agents.")
|
|
104
|
+
print("Press Ctrl+C anytime to cancel.\n")
|
|
105
|
+
|
|
106
|
+
# Auto-link skills based on selected agents
|
|
107
|
+
agent_skills_mapping = {}
|
|
108
|
+
if selected_agents:
|
|
109
|
+
print("š Auto-linking skills based on selected agents...\n")
|
|
110
|
+
agent_skills_mapping = self._auto_link_skills(selected_agents)
|
|
111
|
+
self._display_auto_linked_skills(agent_skills_mapping)
|
|
112
|
+
|
|
113
|
+
# Ask if user wants to customize
|
|
114
|
+
customize = (
|
|
115
|
+
input("\nWould you like to customize skill selections? [y/N]: ")
|
|
116
|
+
.strip()
|
|
117
|
+
.lower()
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if customize in ["y", "yes"]:
|
|
121
|
+
agent_skills_mapping = self._run_custom_selection(
|
|
122
|
+
selected_agents, agent_skills_mapping
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Preview final configuration
|
|
126
|
+
self._preview_final_configuration(agent_skills_mapping)
|
|
127
|
+
|
|
128
|
+
# Confirm
|
|
129
|
+
confirm = (
|
|
130
|
+
input("\nApply this skills configuration? [Y/n]: ").strip().lower()
|
|
131
|
+
)
|
|
132
|
+
if confirm in ["n", "no"]:
|
|
133
|
+
return False, {}
|
|
134
|
+
|
|
135
|
+
# Apply configuration
|
|
136
|
+
self._apply_skills_configuration(agent_skills_mapping)
|
|
137
|
+
|
|
138
|
+
print("\nā
Skills configuration complete!")
|
|
139
|
+
return True, agent_skills_mapping
|
|
140
|
+
|
|
141
|
+
except KeyboardInterrupt:
|
|
142
|
+
print("\n\nā Skills selection cancelled")
|
|
143
|
+
return False, {}
|
|
144
|
+
except Exception as e:
|
|
145
|
+
error_msg = f"Skills selection error: {e}"
|
|
146
|
+
self.logger.error(error_msg, exc_info=True)
|
|
147
|
+
print(f"\nā {error_msg}")
|
|
148
|
+
return False, {}
|
|
149
|
+
|
|
150
|
+
def _auto_link_skills(self, agent_ids: List[str]) -> Dict[str, List[str]]:
|
|
151
|
+
"""Auto-link skills to agents based on agent types.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
agent_ids: List of agent IDs
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Dictionary mapping agent IDs to skill names
|
|
158
|
+
"""
|
|
159
|
+
mapping = {}
|
|
160
|
+
for agent_id in agent_ids:
|
|
161
|
+
# Try to match against known patterns
|
|
162
|
+
skills = self._get_recommended_skills_for_agent(agent_id)
|
|
163
|
+
if skills:
|
|
164
|
+
mapping[agent_id] = skills
|
|
165
|
+
else:
|
|
166
|
+
# Default to core engineer skills if no match
|
|
167
|
+
mapping[agent_id] = ENGINEER_CORE_SKILLS.copy()
|
|
168
|
+
|
|
169
|
+
return mapping
|
|
170
|
+
|
|
171
|
+
def _get_recommended_skills_for_agent(self, agent_id: str) -> List[str]:
|
|
172
|
+
"""Get recommended skills for an agent based on its ID.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
agent_id: Agent identifier
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
List of recommended skill names
|
|
179
|
+
"""
|
|
180
|
+
agent_id_lower = agent_id.lower()
|
|
181
|
+
|
|
182
|
+
# Direct match
|
|
183
|
+
if agent_id_lower in AGENT_SKILL_MAPPING:
|
|
184
|
+
return AGENT_SKILL_MAPPING[agent_id_lower].copy()
|
|
185
|
+
|
|
186
|
+
# Fuzzy matching for common patterns
|
|
187
|
+
if "python" in agent_id_lower:
|
|
188
|
+
return PYTHON_SKILLS.copy()
|
|
189
|
+
if any(js in agent_id_lower for js in ["typescript", "ts", "javascript", "js"]):
|
|
190
|
+
return TYPESCRIPT_SKILLS.copy()
|
|
191
|
+
if "react" in agent_id_lower:
|
|
192
|
+
return REACT_SKILLS.copy()
|
|
193
|
+
if "next" in agent_id_lower:
|
|
194
|
+
return NEXTJS_SKILLS.copy()
|
|
195
|
+
if "vue" in agent_id_lower:
|
|
196
|
+
return VUE_SKILLS.copy()
|
|
197
|
+
if "go" in agent_id_lower or "golang" in agent_id_lower:
|
|
198
|
+
return GOLANG_SKILLS.copy()
|
|
199
|
+
if any(ops in agent_id_lower for ops in ["ops", "devops", "deploy"]):
|
|
200
|
+
return OPS_SKILLS.copy()
|
|
201
|
+
if any(qa in agent_id_lower for qa in ["qa", "test", "quality"]):
|
|
202
|
+
return QA_SKILLS.copy()
|
|
203
|
+
if any(doc in agent_id_lower for doc in ["doc", "writer", "technical"]):
|
|
204
|
+
return DOCUMENTATION_SKILLS.copy()
|
|
205
|
+
if "engineer" in agent_id_lower:
|
|
206
|
+
return ENGINEER_CORE_SKILLS.copy()
|
|
207
|
+
|
|
208
|
+
# Default
|
|
209
|
+
return []
|
|
210
|
+
|
|
211
|
+
def _display_auto_linked_skills(self, mapping: Dict[str, List[str]]):
|
|
212
|
+
"""Display auto-linked skills configuration.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
mapping: Agent-to-skills mapping
|
|
216
|
+
"""
|
|
217
|
+
for agent_id, skills in mapping.items():
|
|
218
|
+
print(f" ⢠{agent_id}:")
|
|
219
|
+
for skill in skills:
|
|
220
|
+
print(f" - {skill}")
|
|
221
|
+
print()
|
|
222
|
+
|
|
223
|
+
def _run_custom_selection(
|
|
224
|
+
self, agent_ids: Optional[List[str]], initial_mapping: Dict[str, List[str]]
|
|
225
|
+
) -> Dict[str, List[str]]:
|
|
226
|
+
"""Run custom skills selection for each agent.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
agent_ids: List of agent IDs
|
|
230
|
+
initial_mapping: Initial auto-linked mapping
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Updated agent-to-skills mapping
|
|
234
|
+
"""
|
|
235
|
+
mapping = initial_mapping.copy()
|
|
236
|
+
|
|
237
|
+
# Get all available bundled skills
|
|
238
|
+
bundled_skills = self.registry.list_skills(source="bundled")
|
|
239
|
+
skill_list = sorted([skill.name for skill in bundled_skills])
|
|
240
|
+
|
|
241
|
+
print("\n" + "=" * 60)
|
|
242
|
+
print("Available Bundled Skills:")
|
|
243
|
+
print("=" * 60)
|
|
244
|
+
for i, skill in enumerate(bundled_skills, 1):
|
|
245
|
+
description = (
|
|
246
|
+
skill.description[:60] + "..."
|
|
247
|
+
if len(skill.description) > 60
|
|
248
|
+
else skill.description
|
|
249
|
+
)
|
|
250
|
+
print(f" [{i:2d}] {skill.name}")
|
|
251
|
+
print(f" {description}")
|
|
252
|
+
print()
|
|
253
|
+
|
|
254
|
+
# If no agents provided, ask which agents to configure
|
|
255
|
+
if not agent_ids:
|
|
256
|
+
agent_ids = self._get_agents_to_configure()
|
|
257
|
+
|
|
258
|
+
# Configure each agent
|
|
259
|
+
for agent_id in agent_ids:
|
|
260
|
+
print(f"\nš§ Configuring skills for: {agent_id}")
|
|
261
|
+
current_skills = mapping.get(agent_id, [])
|
|
262
|
+
|
|
263
|
+
print(f" Current skills ({len(current_skills)}):")
|
|
264
|
+
for skill in current_skills:
|
|
265
|
+
print(f" - {skill}")
|
|
266
|
+
|
|
267
|
+
modify = (
|
|
268
|
+
input(f"\n Modify skills for {agent_id}? [y/N]: ").strip().lower()
|
|
269
|
+
)
|
|
270
|
+
if modify not in ["y", "yes"]:
|
|
271
|
+
continue
|
|
272
|
+
|
|
273
|
+
# Let user select skills
|
|
274
|
+
print("\n Enter skill numbers (comma-separated), or:")
|
|
275
|
+
print(" 'all' - Select all skills")
|
|
276
|
+
print(" 'none' - Clear all skills")
|
|
277
|
+
print(" 'keep' - Keep current selection")
|
|
278
|
+
selection = input(" Selection: ").strip().lower()
|
|
279
|
+
|
|
280
|
+
if selection == "keep":
|
|
281
|
+
continue
|
|
282
|
+
if selection == "none":
|
|
283
|
+
mapping[agent_id] = []
|
|
284
|
+
elif selection == "all":
|
|
285
|
+
mapping[agent_id] = skill_list.copy()
|
|
286
|
+
else:
|
|
287
|
+
# Parse comma-separated numbers
|
|
288
|
+
try:
|
|
289
|
+
selected_indices = [
|
|
290
|
+
int(idx.strip()) for idx in selection.split(",")
|
|
291
|
+
]
|
|
292
|
+
selected_skills = [
|
|
293
|
+
skill_list[idx - 1]
|
|
294
|
+
for idx in selected_indices
|
|
295
|
+
if 1 <= idx <= len(skill_list)
|
|
296
|
+
]
|
|
297
|
+
mapping[agent_id] = selected_skills
|
|
298
|
+
except (ValueError, IndexError) as e:
|
|
299
|
+
print(f" ā ļø Invalid selection, keeping current: {e}")
|
|
300
|
+
|
|
301
|
+
return mapping
|
|
302
|
+
|
|
303
|
+
def _get_agents_to_configure(self) -> List[str]:
|
|
304
|
+
"""Ask user which agents to configure.
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
List of agent IDs
|
|
308
|
+
"""
|
|
309
|
+
agent_ids_input = input("\nEnter agent IDs (comma-separated): ").strip()
|
|
310
|
+
return [aid.strip() for aid in agent_ids_input.split(",") if aid.strip()]
|
|
311
|
+
|
|
312
|
+
def _preview_final_configuration(self, mapping: Dict[str, List[str]]):
|
|
313
|
+
"""Display final skills configuration preview.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
mapping: Agent-to-skills mapping
|
|
317
|
+
"""
|
|
318
|
+
print("\n" + "=" * 60)
|
|
319
|
+
print("š Final Skills Configuration:")
|
|
320
|
+
print("=" * 60)
|
|
321
|
+
|
|
322
|
+
if not mapping:
|
|
323
|
+
print(" (No skills configured)")
|
|
324
|
+
return
|
|
325
|
+
|
|
326
|
+
for agent_id, skills in mapping.items():
|
|
327
|
+
print(f"\n {agent_id} ({len(skills)} skills):")
|
|
328
|
+
if skills:
|
|
329
|
+
for skill in skills:
|
|
330
|
+
print(f" ā {skill}")
|
|
331
|
+
else:
|
|
332
|
+
print(" (no skills)")
|
|
333
|
+
|
|
334
|
+
def _apply_skills_configuration(self, mapping: Dict[str, List[str]]):
|
|
335
|
+
"""Apply skills configuration to skill manager.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
mapping: Agent-to-skills mapping
|
|
339
|
+
"""
|
|
340
|
+
for agent_id, skills in mapping.items():
|
|
341
|
+
# Clear existing mappings for this agent
|
|
342
|
+
if agent_id in self.manager.agent_skill_mapping:
|
|
343
|
+
self.manager.agent_skill_mapping[agent_id] = []
|
|
344
|
+
|
|
345
|
+
# Add each skill
|
|
346
|
+
for skill_name in skills:
|
|
347
|
+
self.manager.add_skill_to_agent(agent_id, skill_name)
|
|
348
|
+
|
|
349
|
+
self.logger.info(f"Applied skills configuration for {len(mapping)} agents")
|
|
350
|
+
|
|
351
|
+
def list_available_skills(self):
|
|
352
|
+
"""Display all available skills."""
|
|
353
|
+
print("\n" + "=" * 60)
|
|
354
|
+
print("š Available Skills")
|
|
355
|
+
print("=" * 60)
|
|
356
|
+
|
|
357
|
+
# Bundled skills
|
|
358
|
+
bundled_skills = self.registry.list_skills(source="bundled")
|
|
359
|
+
if bundled_skills:
|
|
360
|
+
print(f"\nš¹ Bundled Skills ({len(bundled_skills)}):")
|
|
361
|
+
for skill in sorted(bundled_skills, key=lambda s: s.name):
|
|
362
|
+
print(f" ⢠{skill.name}")
|
|
363
|
+
if skill.description:
|
|
364
|
+
desc = (
|
|
365
|
+
skill.description[:80] + "..."
|
|
366
|
+
if len(skill.description) > 80
|
|
367
|
+
else skill.description
|
|
368
|
+
)
|
|
369
|
+
print(f" {desc}")
|
|
370
|
+
|
|
371
|
+
# User skills
|
|
372
|
+
user_skills = self.registry.list_skills(source="user")
|
|
373
|
+
if user_skills:
|
|
374
|
+
print(f"\nš¤ User Skills ({len(user_skills)}):")
|
|
375
|
+
for skill in sorted(user_skills, key=lambda s: s.name):
|
|
376
|
+
print(f" ⢠{skill.name}")
|
|
377
|
+
if skill.description:
|
|
378
|
+
desc = (
|
|
379
|
+
skill.description[:80] + "..."
|
|
380
|
+
if len(skill.description) > 80
|
|
381
|
+
else skill.description
|
|
382
|
+
)
|
|
383
|
+
print(f" {desc}")
|
|
384
|
+
|
|
385
|
+
# Project skills
|
|
386
|
+
project_skills = self.registry.list_skills(source="project")
|
|
387
|
+
if project_skills:
|
|
388
|
+
print(f"\nš Project Skills ({len(project_skills)}):")
|
|
389
|
+
for skill in sorted(project_skills, key=lambda s: s.name):
|
|
390
|
+
print(f" ⢠{skill.name}")
|
|
391
|
+
if skill.description:
|
|
392
|
+
desc = (
|
|
393
|
+
skill.description[:80] + "..."
|
|
394
|
+
if len(skill.description) > 80
|
|
395
|
+
else skill.description
|
|
396
|
+
)
|
|
397
|
+
print(f" {desc}")
|
|
398
|
+
|
|
399
|
+
print()
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def discover_and_link_runtime_skills():
|
|
403
|
+
"""Discover user/project skills and auto-link to agents at runtime.
|
|
404
|
+
|
|
405
|
+
This function is called during startup to:
|
|
406
|
+
1. Reload the skills registry (picks up new skills from .claude/skills/)
|
|
407
|
+
2. Auto-link discovered skills to agents based on tags/naming conventions
|
|
408
|
+
"""
|
|
409
|
+
try:
|
|
410
|
+
registry = get_registry()
|
|
411
|
+
manager = get_manager()
|
|
412
|
+
|
|
413
|
+
# Reload registry to pick up new skills
|
|
414
|
+
registry.reload()
|
|
415
|
+
|
|
416
|
+
# Get discovered skills (user and project)
|
|
417
|
+
discovered_skills = registry.list_skills(source="user") + registry.list_skills(
|
|
418
|
+
source="project"
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
if not discovered_skills:
|
|
422
|
+
logger.debug("No runtime skills discovered")
|
|
423
|
+
return
|
|
424
|
+
|
|
425
|
+
logger.info(f"Discovered {len(discovered_skills)} runtime skills")
|
|
426
|
+
|
|
427
|
+
# Auto-link based on skill content and naming
|
|
428
|
+
for skill in discovered_skills:
|
|
429
|
+
agents = _infer_agents_for_skill(skill)
|
|
430
|
+
for agent_id in agents:
|
|
431
|
+
manager.add_skill_to_agent(agent_id, skill.name)
|
|
432
|
+
logger.debug(f"Auto-linked skill '{skill.name}' to agent '{agent_id}'")
|
|
433
|
+
|
|
434
|
+
except Exception as e:
|
|
435
|
+
logger.error(f"Error during runtime skills discovery: {e}", exc_info=True)
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def _infer_agents_for_skill(skill) -> List[str]:
|
|
439
|
+
"""Infer which agents should have this skill based on tags/name.
|
|
440
|
+
|
|
441
|
+
Args:
|
|
442
|
+
skill: Skill object to analyze
|
|
443
|
+
|
|
444
|
+
Returns:
|
|
445
|
+
List of agent IDs that should have this skill
|
|
446
|
+
"""
|
|
447
|
+
agents = []
|
|
448
|
+
content_lower = skill.content.lower()
|
|
449
|
+
name_lower = skill.name.lower()
|
|
450
|
+
|
|
451
|
+
# Python-related
|
|
452
|
+
if any(
|
|
453
|
+
tag in content_lower or tag in name_lower
|
|
454
|
+
for tag in ["python", "django", "flask", "fastapi"]
|
|
455
|
+
):
|
|
456
|
+
agents.append("python-engineer")
|
|
457
|
+
|
|
458
|
+
# TypeScript/JavaScript-related
|
|
459
|
+
if any(
|
|
460
|
+
tag in content_lower or tag in name_lower
|
|
461
|
+
for tag in ["typescript", "javascript", "react", "next", "vue", "node"]
|
|
462
|
+
):
|
|
463
|
+
agents.extend(["typescript-engineer", "react-engineer", "nextjs-engineer"])
|
|
464
|
+
|
|
465
|
+
# Go-related
|
|
466
|
+
if any(tag in content_lower or tag in name_lower for tag in ["golang", "go "]):
|
|
467
|
+
agents.append("golang-engineer")
|
|
468
|
+
|
|
469
|
+
# Ops-related
|
|
470
|
+
if any(
|
|
471
|
+
tag in content_lower or tag in name_lower
|
|
472
|
+
for tag in ["docker", "kubernetes", "deploy", "devops", "ops"]
|
|
473
|
+
):
|
|
474
|
+
agents.extend(["ops", "devops", "local-ops"])
|
|
475
|
+
|
|
476
|
+
# Testing/QA-related
|
|
477
|
+
if any(
|
|
478
|
+
tag in content_lower or tag in name_lower
|
|
479
|
+
for tag in ["test", "qa", "quality", "assert"]
|
|
480
|
+
):
|
|
481
|
+
agents.extend(["qa", "web-qa", "api-qa"])
|
|
482
|
+
|
|
483
|
+
# Documentation-related
|
|
484
|
+
if any(
|
|
485
|
+
tag in content_lower or tag in name_lower
|
|
486
|
+
for tag in ["documentation", "docs", "api doc", "openapi"]
|
|
487
|
+
):
|
|
488
|
+
agents.extend(["docs", "documentation", "technical-writer"])
|
|
489
|
+
|
|
490
|
+
# Remove duplicates
|
|
491
|
+
return list(set(agents))
|
|
@@ -18,10 +18,16 @@ DESIGN DECISION: Each parser module handles a specific command domain:
|
|
|
18
18
|
- mcp_parser.py: MCP Gateway commands
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
from .base_parser import
|
|
21
|
+
from .base_parser import (
|
|
22
|
+
SuggestingArgumentParser,
|
|
23
|
+
add_common_arguments,
|
|
24
|
+
create_parser,
|
|
25
|
+
preprocess_args,
|
|
26
|
+
)
|
|
22
27
|
from .run_parser import add_run_arguments
|
|
23
28
|
|
|
24
29
|
__all__ = [
|
|
30
|
+
"SuggestingArgumentParser",
|
|
25
31
|
"add_common_arguments",
|
|
26
32
|
"add_run_arguments",
|
|
27
33
|
"create_parser",
|