claude-mpm 4.3.6__py3-none-any.whl → 4.3.12__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_PM.md +41 -8
- claude_mpm/agents/PM_INSTRUCTIONS.md +85 -43
- claude_mpm/agents/templates/clerk-ops.json +223 -0
- claude_mpm/agents/templates/data_engineer.json +41 -5
- claude_mpm/agents/templates/php-engineer.json +185 -0
- claude_mpm/agents/templates/research.json +20 -8
- claude_mpm/agents/templates/web_qa.json +25 -10
- claude_mpm/cli/__init__.py +41 -2
- claude_mpm/cli/commands/agents.py +2 -2
- claude_mpm/cli/commands/analyze.py +4 -4
- claude_mpm/cli/commands/cleanup.py +7 -7
- claude_mpm/cli/commands/configure_tui.py +2 -2
- claude_mpm/cli/commands/debug.py +2 -2
- claude_mpm/cli/commands/info.py +3 -4
- claude_mpm/cli/commands/mcp.py +8 -6
- claude_mpm/cli/commands/mcp_command_router.py +11 -0
- claude_mpm/cli/commands/mcp_config.py +157 -0
- claude_mpm/cli/commands/mcp_external_commands.py +241 -0
- claude_mpm/cli/commands/mcp_install_commands.py +73 -32
- claude_mpm/cli/commands/mcp_setup_external.py +829 -0
- claude_mpm/cli/commands/run.py +73 -3
- claude_mpm/cli/commands/search.py +285 -0
- claude_mpm/cli/parsers/base_parser.py +13 -0
- claude_mpm/cli/parsers/mcp_parser.py +17 -0
- claude_mpm/cli/parsers/run_parser.py +5 -0
- claude_mpm/cli/parsers/search_parser.py +239 -0
- claude_mpm/cli/startup_logging.py +20 -7
- claude_mpm/constants.py +1 -0
- claude_mpm/core/unified_agent_registry.py +7 -0
- claude_mpm/hooks/instruction_reinforcement.py +295 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +28 -13
- claude_mpm/services/agents/deployment/agent_discovery_service.py +16 -6
- claude_mpm/services/agents/deployment/deployment_wrapper.py +59 -0
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +6 -4
- claude_mpm/services/cli/agent_cleanup_service.py +5 -0
- claude_mpm/services/mcp_config_manager.py +294 -0
- claude_mpm/services/mcp_gateway/config/configuration.py +17 -0
- claude_mpm/services/mcp_gateway/main.py +38 -0
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +390 -0
- claude_mpm/utils/log_cleanup.py +17 -17
- claude_mpm/utils/subprocess_utils.py +6 -6
- {claude_mpm-4.3.6.dist-info → claude_mpm-4.3.12.dist-info}/METADATA +24 -1
- {claude_mpm-4.3.6.dist-info → claude_mpm-4.3.12.dist-info}/RECORD +48 -39
- claude_mpm/agents/templates/agent-manager.md +0 -619
- {claude_mpm-4.3.6.dist-info → claude_mpm-4.3.12.dist-info}/WHEEL +0 -0
- {claude_mpm-4.3.6.dist-info → claude_mpm-4.3.12.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.3.6.dist-info → claude_mpm-4.3.12.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.3.6.dist-info → claude_mpm-4.3.12.dist-info}/top_level.txt +0 -0
claude_mpm/cli/commands/run.py
CHANGED
|
@@ -15,7 +15,7 @@ DESIGN DECISIONS:
|
|
|
15
15
|
|
|
16
16
|
import subprocess
|
|
17
17
|
import sys
|
|
18
|
-
from datetime import datetime
|
|
18
|
+
from datetime import datetime, timezone
|
|
19
19
|
from typing import Optional
|
|
20
20
|
|
|
21
21
|
from ...constants import LogLevel
|
|
@@ -68,6 +68,7 @@ def filter_claude_mpm_args(claude_args):
|
|
|
68
68
|
"--no-native-agents",
|
|
69
69
|
"--launch-method",
|
|
70
70
|
"--mpm-resume",
|
|
71
|
+
"--reload-agents", # New flag to force rebuild system agents
|
|
71
72
|
# Dependency checking flags (MPM-specific)
|
|
72
73
|
"--no-check-dependencies",
|
|
73
74
|
"--force-check-dependencies",
|
|
@@ -522,7 +523,7 @@ class RunCommand(BaseCommand):
|
|
|
522
523
|
# Update session usage
|
|
523
524
|
session = session_manager.load_session(resume_session_id)
|
|
524
525
|
if session:
|
|
525
|
-
session.last_used = datetime.now().isoformat()
|
|
526
|
+
session.last_used = datetime.now(timezone.utc).isoformat()
|
|
526
527
|
session.use_count += 1
|
|
527
528
|
session_manager.save_session(session)
|
|
528
529
|
else:
|
|
@@ -568,6 +569,71 @@ class RunCommand(BaseCommand):
|
|
|
568
569
|
return False
|
|
569
570
|
|
|
570
571
|
|
|
572
|
+
def _handle_reload_agents(logger):
|
|
573
|
+
"""
|
|
574
|
+
Handle the --reload-agents flag by deleting all local claude-mpm system agents.
|
|
575
|
+
|
|
576
|
+
This forces a fresh rebuild of system agents on the next deployment,
|
|
577
|
+
while preserving user-created agents.
|
|
578
|
+
|
|
579
|
+
Args:
|
|
580
|
+
logger: Logger instance for output
|
|
581
|
+
"""
|
|
582
|
+
try:
|
|
583
|
+
logger.info("Reloading system agents - cleaning existing deployments...")
|
|
584
|
+
|
|
585
|
+
# Import the cleanup service
|
|
586
|
+
from ...services.cli.agent_cleanup_service import AgentCleanupService
|
|
587
|
+
from ...services.agents.deployment.agent_deployment import AgentDeploymentService
|
|
588
|
+
|
|
589
|
+
# Create services
|
|
590
|
+
deployment_service = AgentDeploymentService()
|
|
591
|
+
cleanup_service = AgentCleanupService(deployment_service)
|
|
592
|
+
|
|
593
|
+
# Determine the agents directory
|
|
594
|
+
agents_dir = None # Will auto-detect project or user directory
|
|
595
|
+
|
|
596
|
+
# Clean deployed agents (preserves user agents)
|
|
597
|
+
result = cleanup_service.clean_deployed_agents(agents_dir)
|
|
598
|
+
|
|
599
|
+
# Check if cleanup was successful based on the result structure
|
|
600
|
+
# The service returns a dict with 'removed', 'preserved', and possibly 'errors' keys
|
|
601
|
+
# If it has 'success' key, use it; otherwise infer from the result
|
|
602
|
+
success = result.get("success", True) if "success" in result else not result.get("errors")
|
|
603
|
+
|
|
604
|
+
if success:
|
|
605
|
+
removed_count = result.get("cleaned_count", len(result.get("removed", [])))
|
|
606
|
+
removed_agents = result.get("removed", [])
|
|
607
|
+
preserved_agents = result.get("preserved", [])
|
|
608
|
+
|
|
609
|
+
if removed_count > 0:
|
|
610
|
+
logger.info(f"✅ Successfully removed {removed_count} system agents")
|
|
611
|
+
if removed_agents:
|
|
612
|
+
logger.debug(f"Removed agents: {', '.join(removed_agents)}")
|
|
613
|
+
print(f"🔄 Cleaned {removed_count} claude-mpm system agents")
|
|
614
|
+
else:
|
|
615
|
+
logger.info("No system agents found to clean")
|
|
616
|
+
print("ℹ️ No system agents found - already clean")
|
|
617
|
+
|
|
618
|
+
if preserved_agents:
|
|
619
|
+
logger.info(f"Preserved {len(preserved_agents)} user-created agents")
|
|
620
|
+
print(f"✅ Preserved {len(preserved_agents)} user-created agents")
|
|
621
|
+
|
|
622
|
+
print("🚀 System agents will be rebuilt on next use")
|
|
623
|
+
else:
|
|
624
|
+
error = result.get("error", "Cleanup failed")
|
|
625
|
+
if result.get("errors"):
|
|
626
|
+
error = f"Cleanup errors: {', '.join(result['errors'])}"
|
|
627
|
+
logger.error(f"Failed to clean system agents: {error}")
|
|
628
|
+
print(f"❌ Error cleaning agents: {error}")
|
|
629
|
+
|
|
630
|
+
except Exception as e:
|
|
631
|
+
logger.error(f"Error handling --reload-agents: {e}", exc_info=True)
|
|
632
|
+
print(f"❌ Failed to reload agents: {e}")
|
|
633
|
+
# Don't fail the entire session, just log the error
|
|
634
|
+
print("⚠️ Continuing with existing agents...")
|
|
635
|
+
|
|
636
|
+
|
|
571
637
|
def run_session(args):
|
|
572
638
|
"""
|
|
573
639
|
Main entry point for run command.
|
|
@@ -624,6 +690,10 @@ def run_session_legacy(args):
|
|
|
624
690
|
# Check for memory usage issues with .claude.json
|
|
625
691
|
_check_claude_json_memory(args, logger)
|
|
626
692
|
|
|
693
|
+
# Handle --reload-agents flag if specified
|
|
694
|
+
if getattr(args, "reload_agents", False):
|
|
695
|
+
_handle_reload_agents(logger)
|
|
696
|
+
|
|
627
697
|
try:
|
|
628
698
|
from ...core.claude_runner import ClaudeRunner, create_simple_context
|
|
629
699
|
except ImportError:
|
|
@@ -936,7 +1006,7 @@ def run_session_legacy(args):
|
|
|
936
1006
|
# Update session usage
|
|
937
1007
|
session = session_manager.load_session(resume_session_id)
|
|
938
1008
|
if session:
|
|
939
|
-
session.last_used = datetime.now().isoformat()
|
|
1009
|
+
session.last_used = datetime.now(timezone.utc).isoformat()
|
|
940
1010
|
session.use_count += 1
|
|
941
1011
|
session_manager.save_session(session)
|
|
942
1012
|
else:
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Search command module for mcp-vector-search integration.
|
|
3
|
+
|
|
4
|
+
This module provides the /mpm-search command for semantic code search
|
|
5
|
+
using the mcp-vector-search service.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
import sys
|
|
11
|
+
from typing import Any, Dict, Optional
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
from rich.panel import Panel
|
|
16
|
+
from rich.syntax import Syntax
|
|
17
|
+
from rich.table import Table
|
|
18
|
+
|
|
19
|
+
from claude_mpm.cli.utils import handle_async_errors
|
|
20
|
+
from claude_mpm.services.service_container import get_service_container
|
|
21
|
+
|
|
22
|
+
console = Console()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MCPSearchInterface:
|
|
26
|
+
"""Interface for interacting with mcp-vector-search service."""
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
"""Initialize the search interface."""
|
|
30
|
+
self.container = get_service_container()
|
|
31
|
+
self.mcp_gateway = None
|
|
32
|
+
|
|
33
|
+
async def initialize(self):
|
|
34
|
+
"""Initialize the MCP gateway connection."""
|
|
35
|
+
try:
|
|
36
|
+
from claude_mpm.services.mcp_gateway import MCPGatewayService
|
|
37
|
+
self.mcp_gateway = self.container.resolve(MCPGatewayService)
|
|
38
|
+
if not self.mcp_gateway:
|
|
39
|
+
self.mcp_gateway = MCPGatewayService()
|
|
40
|
+
await self.mcp_gateway.initialize()
|
|
41
|
+
except Exception as e:
|
|
42
|
+
console.print(f"[red]Failed to initialize MCP gateway: {e}[/red]")
|
|
43
|
+
raise
|
|
44
|
+
|
|
45
|
+
async def search_code(
|
|
46
|
+
self,
|
|
47
|
+
query: str,
|
|
48
|
+
limit: int = 10,
|
|
49
|
+
similarity_threshold: float = 0.3,
|
|
50
|
+
file_extensions: Optional[list] = None,
|
|
51
|
+
language: Optional[str] = None
|
|
52
|
+
) -> Dict[str, Any]:
|
|
53
|
+
"""Search code using semantic similarity."""
|
|
54
|
+
params = {
|
|
55
|
+
"query": query,
|
|
56
|
+
"limit": limit,
|
|
57
|
+
"similarity_threshold": similarity_threshold
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if file_extensions:
|
|
61
|
+
params["file_extensions"] = file_extensions
|
|
62
|
+
if language:
|
|
63
|
+
params["language"] = language
|
|
64
|
+
|
|
65
|
+
return await self._call_mcp_tool("mcp__mcp-vector-search__search_code", params)
|
|
66
|
+
|
|
67
|
+
async def search_similar(
|
|
68
|
+
self,
|
|
69
|
+
file_path: str,
|
|
70
|
+
function_name: Optional[str] = None,
|
|
71
|
+
limit: int = 10,
|
|
72
|
+
similarity_threshold: float = 0.3
|
|
73
|
+
) -> Dict[str, Any]:
|
|
74
|
+
"""Find code similar to a specific file or function."""
|
|
75
|
+
params = {
|
|
76
|
+
"file_path": file_path,
|
|
77
|
+
"limit": limit,
|
|
78
|
+
"similarity_threshold": similarity_threshold
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if function_name:
|
|
82
|
+
params["function_name"] = function_name
|
|
83
|
+
|
|
84
|
+
return await self._call_mcp_tool("mcp__mcp-vector-search__search_similar", params)
|
|
85
|
+
|
|
86
|
+
async def search_context(
|
|
87
|
+
self,
|
|
88
|
+
description: str,
|
|
89
|
+
focus_areas: Optional[list] = None,
|
|
90
|
+
limit: int = 10
|
|
91
|
+
) -> Dict[str, Any]:
|
|
92
|
+
"""Search for code based on contextual description."""
|
|
93
|
+
params = {
|
|
94
|
+
"description": description,
|
|
95
|
+
"limit": limit
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if focus_areas:
|
|
99
|
+
params["focus_areas"] = focus_areas
|
|
100
|
+
|
|
101
|
+
return await self._call_mcp_tool("mcp__mcp-vector-search__search_context", params)
|
|
102
|
+
|
|
103
|
+
async def get_status(self) -> Dict[str, Any]:
|
|
104
|
+
"""Get project indexing status and statistics."""
|
|
105
|
+
return await self._call_mcp_tool("mcp__mcp-vector-search__get_project_status", {})
|
|
106
|
+
|
|
107
|
+
async def index_project(
|
|
108
|
+
self,
|
|
109
|
+
force: bool = False,
|
|
110
|
+
file_extensions: Optional[list] = None
|
|
111
|
+
) -> Dict[str, Any]:
|
|
112
|
+
"""Index or reindex the project codebase."""
|
|
113
|
+
params = {"force": force}
|
|
114
|
+
|
|
115
|
+
if file_extensions:
|
|
116
|
+
params["file_extensions"] = file_extensions
|
|
117
|
+
|
|
118
|
+
return await self._call_mcp_tool("mcp__mcp-vector-search__index_project", params)
|
|
119
|
+
|
|
120
|
+
async def _call_mcp_tool(self, tool_name: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
121
|
+
"""Call an MCP tool through the gateway."""
|
|
122
|
+
if not self.mcp_gateway:
|
|
123
|
+
await self.initialize()
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
result = await self.mcp_gateway.call_tool(tool_name, params)
|
|
127
|
+
return result
|
|
128
|
+
except Exception as e:
|
|
129
|
+
return {"error": str(e)}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def display_search_results(results: Dict[str, Any], output_format: str = "rich"):
|
|
133
|
+
"""Display search results in the specified format."""
|
|
134
|
+
if output_format == "json":
|
|
135
|
+
console.print_json(json.dumps(results, indent=2))
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
if "error" in results:
|
|
139
|
+
console.print(f"[red]Error: {results['error']}[/red]")
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
if not results.get("results"):
|
|
143
|
+
console.print("[yellow]No results found.[/yellow]")
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
# Create a table for results
|
|
147
|
+
table = Table(title="Search Results", show_header=True, header_style="bold magenta")
|
|
148
|
+
table.add_column("File", style="cyan", no_wrap=False)
|
|
149
|
+
table.add_column("Score", style="green", width=8)
|
|
150
|
+
table.add_column("Type", style="yellow", width=10)
|
|
151
|
+
table.add_column("Name", style="blue", no_wrap=False)
|
|
152
|
+
|
|
153
|
+
for result in results["results"]:
|
|
154
|
+
file_path = result.get("file_path", "Unknown")
|
|
155
|
+
score = f"{result.get('score', 0):.3f}"
|
|
156
|
+
item_type = result.get("type", "unknown")
|
|
157
|
+
name = result.get("name", result.get("function_name", ""))
|
|
158
|
+
|
|
159
|
+
table.add_row(file_path, score, item_type, name)
|
|
160
|
+
|
|
161
|
+
# Show snippet if available
|
|
162
|
+
if result.get("snippet"):
|
|
163
|
+
snippet_panel = Panel(
|
|
164
|
+
Syntax(result["snippet"], result.get("language", "python"), theme="monokai"),
|
|
165
|
+
title=f"[cyan]{file_path}[/cyan]",
|
|
166
|
+
border_style="dim"
|
|
167
|
+
)
|
|
168
|
+
console.print(snippet_panel)
|
|
169
|
+
|
|
170
|
+
console.print(table)
|
|
171
|
+
|
|
172
|
+
# Show statistics if available
|
|
173
|
+
if "stats" in results:
|
|
174
|
+
stats = results["stats"]
|
|
175
|
+
console.print(f"\n[bold]Statistics:[/bold]")
|
|
176
|
+
console.print(f" Total indexed files: {stats.get('total_files', 0)}")
|
|
177
|
+
console.print(f" Total indexed functions: {stats.get('total_functions', 0)}")
|
|
178
|
+
console.print(f" Index last updated: {stats.get('last_updated', 'Unknown')}")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@click.command()
|
|
182
|
+
@click.argument("query", required=False)
|
|
183
|
+
@click.option("--similar", "-s", help="Find code similar to a specific file")
|
|
184
|
+
@click.option("--context", "-c", help="Search by contextual description")
|
|
185
|
+
@click.option("--index", is_flag=True, help="Index or reindex the project")
|
|
186
|
+
@click.option("--status", is_flag=True, help="Check index status")
|
|
187
|
+
@click.option("--limit", "-l", default=10, help="Maximum number of results")
|
|
188
|
+
@click.option("--threshold", "-t", default=0.3, help="Similarity threshold (0.0-1.0)")
|
|
189
|
+
@click.option("--language", help="Filter by programming language")
|
|
190
|
+
@click.option("--extensions", multiple=True, help="Filter by file extensions")
|
|
191
|
+
@click.option("--function", "-f", help="Function name (with --similar)")
|
|
192
|
+
@click.option("--focus", multiple=True, help="Focus areas (with --context)")
|
|
193
|
+
@click.option("--force", is_flag=True, help="Force reindexing (with --index)")
|
|
194
|
+
@click.option("--json", "output_json", is_flag=True, help="Output results as JSON")
|
|
195
|
+
@handle_async_errors
|
|
196
|
+
async def search_command(
|
|
197
|
+
query: Optional[str],
|
|
198
|
+
similar: Optional[str],
|
|
199
|
+
context: Optional[str],
|
|
200
|
+
index: bool,
|
|
201
|
+
status: bool,
|
|
202
|
+
limit: int,
|
|
203
|
+
threshold: float,
|
|
204
|
+
language: Optional[str],
|
|
205
|
+
extensions: tuple,
|
|
206
|
+
function: Optional[str],
|
|
207
|
+
focus: tuple,
|
|
208
|
+
force: bool,
|
|
209
|
+
output_json: bool
|
|
210
|
+
):
|
|
211
|
+
"""
|
|
212
|
+
Search the codebase using semantic search powered by mcp-vector-search.
|
|
213
|
+
|
|
214
|
+
Examples:
|
|
215
|
+
/mpm-search "authentication logic"
|
|
216
|
+
/mpm-search --similar src/auth.py
|
|
217
|
+
/mpm-search --context "find all API endpoints"
|
|
218
|
+
/mpm-search --index --force
|
|
219
|
+
/mpm-search --status
|
|
220
|
+
"""
|
|
221
|
+
search = MCPSearchInterface()
|
|
222
|
+
await search.initialize()
|
|
223
|
+
|
|
224
|
+
output_format = "json" if output_json else "rich"
|
|
225
|
+
|
|
226
|
+
try:
|
|
227
|
+
# Handle different operation modes
|
|
228
|
+
if index:
|
|
229
|
+
console.print("[cyan]Indexing project...[/cyan]")
|
|
230
|
+
result = await search.index_project(
|
|
231
|
+
force=force,
|
|
232
|
+
file_extensions=list(extensions) if extensions else None
|
|
233
|
+
)
|
|
234
|
+
if "error" not in result:
|
|
235
|
+
console.print("[green]✓ Project indexed successfully[/green]")
|
|
236
|
+
display_search_results(result, output_format)
|
|
237
|
+
|
|
238
|
+
elif status:
|
|
239
|
+
result = await search.get_status()
|
|
240
|
+
display_search_results(result, output_format)
|
|
241
|
+
|
|
242
|
+
elif similar:
|
|
243
|
+
result = await search.search_similar(
|
|
244
|
+
file_path=similar,
|
|
245
|
+
function_name=function,
|
|
246
|
+
limit=limit,
|
|
247
|
+
similarity_threshold=threshold
|
|
248
|
+
)
|
|
249
|
+
display_search_results(result, output_format)
|
|
250
|
+
|
|
251
|
+
elif context:
|
|
252
|
+
result = await search.search_context(
|
|
253
|
+
description=context,
|
|
254
|
+
focus_areas=list(focus) if focus else None,
|
|
255
|
+
limit=limit
|
|
256
|
+
)
|
|
257
|
+
display_search_results(result, output_format)
|
|
258
|
+
|
|
259
|
+
elif query:
|
|
260
|
+
result = await search.search_code(
|
|
261
|
+
query=query,
|
|
262
|
+
limit=limit,
|
|
263
|
+
similarity_threshold=threshold,
|
|
264
|
+
file_extensions=list(extensions) if extensions else None,
|
|
265
|
+
language=language
|
|
266
|
+
)
|
|
267
|
+
display_search_results(result, output_format)
|
|
268
|
+
|
|
269
|
+
else:
|
|
270
|
+
console.print("[yellow]No search operation specified. Use --help for options.[/yellow]")
|
|
271
|
+
|
|
272
|
+
except Exception as e:
|
|
273
|
+
console.print(f"[red]Search failed: {e}[/red]")
|
|
274
|
+
if not output_json:
|
|
275
|
+
console.print("[dim]Tip: Make sure the project is indexed with --index first[/dim]")
|
|
276
|
+
sys.exit(1)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def main():
|
|
280
|
+
"""Main entry point for the search command."""
|
|
281
|
+
asyncio.run(search_command())
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
if __name__ == "__main__":
|
|
285
|
+
main()
|
|
@@ -199,6 +199,11 @@ def add_top_level_run_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
199
199
|
action="store_true",
|
|
200
200
|
help="Force operations even with warnings (e.g., large .claude.json file)",
|
|
201
201
|
)
|
|
202
|
+
run_group.add_argument(
|
|
203
|
+
"--reload-agents",
|
|
204
|
+
action="store_true",
|
|
205
|
+
help="Force rebuild of all system agents by deleting local claude-mpm agents",
|
|
206
|
+
)
|
|
202
207
|
|
|
203
208
|
# Dependency checking options (for backward compatibility at top level)
|
|
204
209
|
dep_group_top = parser.add_argument_group(
|
|
@@ -390,6 +395,14 @@ def create_parser(
|
|
|
390
395
|
except ImportError:
|
|
391
396
|
pass
|
|
392
397
|
|
|
398
|
+
# Add search command parser
|
|
399
|
+
try:
|
|
400
|
+
from .search_parser import add_search_subparser
|
|
401
|
+
|
|
402
|
+
add_search_subparser(subparsers)
|
|
403
|
+
except ImportError:
|
|
404
|
+
pass
|
|
405
|
+
|
|
393
406
|
# Import and add additional command parsers from commands module
|
|
394
407
|
try:
|
|
395
408
|
from ..commands.aggregate import add_aggregate_parser
|
|
@@ -177,4 +177,21 @@ def add_mcp_subparser(subparsers) -> argparse.ArgumentParser:
|
|
|
177
177
|
help="Show setup instructions for Claude Code",
|
|
178
178
|
)
|
|
179
179
|
|
|
180
|
+
# External MCP services management
|
|
181
|
+
external_mcp_parser = mcp_subparsers.add_parser(
|
|
182
|
+
MCPCommands.EXTERNAL.value, help="Manage external MCP services"
|
|
183
|
+
)
|
|
184
|
+
external_mcp_parser.add_argument(
|
|
185
|
+
"external_action",
|
|
186
|
+
nargs="?",
|
|
187
|
+
choices=["setup", "list", "check", "fix-browser", "detect"],
|
|
188
|
+
default="list",
|
|
189
|
+
help="External service action (default: list)",
|
|
190
|
+
)
|
|
191
|
+
external_mcp_parser.add_argument(
|
|
192
|
+
"--force",
|
|
193
|
+
action="store_true",
|
|
194
|
+
help="Force overwrite existing configuration"
|
|
195
|
+
)
|
|
196
|
+
|
|
180
197
|
return mcp_parser
|
|
@@ -68,6 +68,11 @@ def add_run_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
68
68
|
action="store_true",
|
|
69
69
|
help="Force operations even with warnings (e.g., large .claude.json file)",
|
|
70
70
|
)
|
|
71
|
+
run_group.add_argument(
|
|
72
|
+
"--reload-agents",
|
|
73
|
+
action="store_true",
|
|
74
|
+
help="Force rebuild of all system agents by deleting local claude-mpm agents",
|
|
75
|
+
)
|
|
71
76
|
run_group.add_argument(
|
|
72
77
|
"--mpm-resume",
|
|
73
78
|
type=str,
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Search command parser for mcp-vector-search integration.
|
|
3
|
+
|
|
4
|
+
This module provides argument parsing for the /mpm-search command.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def add_search_subparser(subparsers: argparse._SubParsersAction) -> argparse.ArgumentParser:
|
|
12
|
+
"""
|
|
13
|
+
Add the search command parser.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
subparsers: The subparsers action to add the search parser to.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
The created search parser.
|
|
20
|
+
"""
|
|
21
|
+
search_parser = subparsers.add_parser(
|
|
22
|
+
"mpm-search",
|
|
23
|
+
aliases=["search"],
|
|
24
|
+
help="Search codebase using semantic search",
|
|
25
|
+
description=(
|
|
26
|
+
"Search the codebase using semantic search powered by mcp-vector-search. "
|
|
27
|
+
"Can search by query, find similar code, search by context, or manage the search index."
|
|
28
|
+
),
|
|
29
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
30
|
+
epilog="""
|
|
31
|
+
Examples:
|
|
32
|
+
# Search for code by query
|
|
33
|
+
claude-mpm mpm-search "authentication logic"
|
|
34
|
+
|
|
35
|
+
# Find code similar to a file
|
|
36
|
+
claude-mpm mpm-search --similar src/auth.py
|
|
37
|
+
|
|
38
|
+
# Search by contextual description
|
|
39
|
+
claude-mpm mpm-search --context "find all API endpoints"
|
|
40
|
+
|
|
41
|
+
# Index the project (required before searching)
|
|
42
|
+
claude-mpm mpm-search --index
|
|
43
|
+
|
|
44
|
+
# Force reindex the project
|
|
45
|
+
claude-mpm mpm-search --index --force
|
|
46
|
+
|
|
47
|
+
# Check index status
|
|
48
|
+
claude-mpm mpm-search --status
|
|
49
|
+
|
|
50
|
+
# Search with filters
|
|
51
|
+
claude-mpm mpm-search "database" --language python --limit 20
|
|
52
|
+
|
|
53
|
+
# Search with multiple file extensions
|
|
54
|
+
claude-mpm mpm-search "test" --extensions .py --extensions .js
|
|
55
|
+
|
|
56
|
+
# Find similar code to a specific function
|
|
57
|
+
claude-mpm mpm-search --similar src/auth.py --function authenticate_user
|
|
58
|
+
|
|
59
|
+
# Search with context and focus areas
|
|
60
|
+
claude-mpm mpm-search --context "security vulnerabilities" --focus authentication --focus encryption
|
|
61
|
+
|
|
62
|
+
# Output as JSON for processing
|
|
63
|
+
claude-mpm mpm-search "api" --json
|
|
64
|
+
""",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Primary search modes (mutually exclusive)
|
|
68
|
+
search_mode = search_parser.add_mutually_exclusive_group()
|
|
69
|
+
|
|
70
|
+
search_mode.add_argument(
|
|
71
|
+
"query",
|
|
72
|
+
nargs="?",
|
|
73
|
+
help="Search query for semantic code search",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
search_mode.add_argument(
|
|
77
|
+
"--similar",
|
|
78
|
+
"-s",
|
|
79
|
+
metavar="FILE",
|
|
80
|
+
help="Find code similar to the specified file",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
search_mode.add_argument(
|
|
84
|
+
"--context",
|
|
85
|
+
"-c",
|
|
86
|
+
metavar="DESCRIPTION",
|
|
87
|
+
help="Search by contextual description of what you're looking for",
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Index management options
|
|
91
|
+
search_parser.add_argument(
|
|
92
|
+
"--index",
|
|
93
|
+
action="store_true",
|
|
94
|
+
help="Index or reindex the project codebase",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
search_parser.add_argument(
|
|
98
|
+
"--status",
|
|
99
|
+
action="store_true",
|
|
100
|
+
help="Check project indexing status and statistics",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Search filters and options
|
|
104
|
+
search_parser.add_argument(
|
|
105
|
+
"--limit",
|
|
106
|
+
"-l",
|
|
107
|
+
type=int,
|
|
108
|
+
default=10,
|
|
109
|
+
metavar="N",
|
|
110
|
+
help="Maximum number of results to return (default: 10, max: 50)",
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
search_parser.add_argument(
|
|
114
|
+
"--threshold",
|
|
115
|
+
"-t",
|
|
116
|
+
type=float,
|
|
117
|
+
default=0.3,
|
|
118
|
+
metavar="SCORE",
|
|
119
|
+
help="Similarity threshold between 0.0 and 1.0 (default: 0.3)",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
search_parser.add_argument(
|
|
123
|
+
"--language",
|
|
124
|
+
metavar="LANG",
|
|
125
|
+
help="Filter by programming language (e.g., python, javascript, go)",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
search_parser.add_argument(
|
|
129
|
+
"--extensions",
|
|
130
|
+
action="append",
|
|
131
|
+
metavar="EXT",
|
|
132
|
+
help="Filter by file extensions (e.g., .py, .js). Can be specified multiple times",
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Options for --similar mode
|
|
136
|
+
search_parser.add_argument(
|
|
137
|
+
"--function",
|
|
138
|
+
"-f",
|
|
139
|
+
metavar="NAME",
|
|
140
|
+
help="Function name within the file (used with --similar)",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Options for --context mode
|
|
144
|
+
search_parser.add_argument(
|
|
145
|
+
"--focus",
|
|
146
|
+
action="append",
|
|
147
|
+
metavar="AREA",
|
|
148
|
+
help="Focus areas for contextual search (e.g., security, performance). Can be specified multiple times",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Options for --index mode
|
|
152
|
+
search_parser.add_argument(
|
|
153
|
+
"--force",
|
|
154
|
+
action="store_true",
|
|
155
|
+
help="Force reindexing even if index already exists (used with --index)",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Output options
|
|
159
|
+
search_parser.add_argument(
|
|
160
|
+
"--json",
|
|
161
|
+
action="store_true",
|
|
162
|
+
dest="output_json",
|
|
163
|
+
help="Output results as JSON instead of formatted text",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# Additional filters
|
|
167
|
+
search_parser.add_argument(
|
|
168
|
+
"--class",
|
|
169
|
+
dest="class_name",
|
|
170
|
+
metavar="NAME",
|
|
171
|
+
help="Filter by class name",
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
search_parser.add_argument(
|
|
175
|
+
"--files",
|
|
176
|
+
metavar="PATTERN",
|
|
177
|
+
help="Filter by file patterns (e.g., '*.py' or 'src/*.js')",
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Verbose output
|
|
181
|
+
search_parser.add_argument(
|
|
182
|
+
"-v",
|
|
183
|
+
"--verbose",
|
|
184
|
+
action="store_true",
|
|
185
|
+
help="Enable verbose output with additional details",
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
return search_parser
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def validate_search_args(args: argparse.Namespace) -> Optional[str]:
|
|
192
|
+
"""
|
|
193
|
+
Validate search command arguments.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
args: Parsed command line arguments.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
Error message if validation fails, None otherwise.
|
|
200
|
+
"""
|
|
201
|
+
# Check threshold is in valid range
|
|
202
|
+
if hasattr(args, "threshold") and args.threshold is not None:
|
|
203
|
+
if not 0.0 <= args.threshold <= 1.0:
|
|
204
|
+
return "Similarity threshold must be between 0.0 and 1.0"
|
|
205
|
+
|
|
206
|
+
# Check limit is reasonable
|
|
207
|
+
if hasattr(args, "limit") and args.limit is not None:
|
|
208
|
+
if args.limit < 1:
|
|
209
|
+
return "Limit must be at least 1"
|
|
210
|
+
if args.limit > 50:
|
|
211
|
+
return "Limit cannot exceed 50"
|
|
212
|
+
|
|
213
|
+
# Check that function is only used with --similar
|
|
214
|
+
if hasattr(args, "function") and args.function and not getattr(args, "similar", None):
|
|
215
|
+
return "--function can only be used with --similar"
|
|
216
|
+
|
|
217
|
+
# Check that focus is only used with --context
|
|
218
|
+
if hasattr(args, "focus") and args.focus and not getattr(args, "context", None):
|
|
219
|
+
return "--focus can only be used with --context"
|
|
220
|
+
|
|
221
|
+
# Check that force is only used with --index
|
|
222
|
+
if hasattr(args, "force") and args.force and not getattr(args, "index", False):
|
|
223
|
+
return "--force can only be used with --index"
|
|
224
|
+
|
|
225
|
+
# Ensure at least one operation is specified
|
|
226
|
+
if hasattr(args, "command") and args.command in ["mpm-search", "search"]:
|
|
227
|
+
has_operation = any(
|
|
228
|
+
[
|
|
229
|
+
getattr(args, "query", None),
|
|
230
|
+
getattr(args, "similar", None),
|
|
231
|
+
getattr(args, "context", None),
|
|
232
|
+
getattr(args, "index", False),
|
|
233
|
+
getattr(args, "status", False),
|
|
234
|
+
]
|
|
235
|
+
)
|
|
236
|
+
if not has_operation:
|
|
237
|
+
return "No search operation specified. Use --help for options."
|
|
238
|
+
|
|
239
|
+
return None
|