mcp-vector-search 0.12.0__py3-none-any.whl → 0.12.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mcp-vector-search might be problematic. Click here for more details.
- mcp_vector_search/__init__.py +2 -2
- mcp_vector_search/cli/commands/index.py +15 -24
- mcp_vector_search/cli/commands/install.py +502 -523
- mcp_vector_search/cli/commands/install_old.py +696 -0
- mcp_vector_search/cli/commands/status.py +7 -5
- mcp_vector_search/cli/commands/uninstall.py +485 -0
- mcp_vector_search/cli/commands/visualize.py +406 -120
- mcp_vector_search/cli/didyoumean.py +10 -0
- mcp_vector_search/cli/main.py +39 -21
- mcp_vector_search/core/connection_pool.py +49 -11
- mcp_vector_search/core/database.py +7 -9
- mcp_vector_search/core/directory_index.py +26 -11
- mcp_vector_search/core/indexer.py +89 -29
- mcp_vector_search/core/models.py +4 -1
- mcp_vector_search/core/project.py +16 -5
- mcp_vector_search/parsers/base.py +54 -18
- mcp_vector_search/parsers/javascript.py +41 -20
- mcp_vector_search/parsers/python.py +19 -11
- mcp_vector_search/parsers/registry.py +3 -2
- mcp_vector_search/utils/gitignore.py +3 -1
- {mcp_vector_search-0.12.0.dist-info → mcp_vector_search-0.12.1.dist-info}/METADATA +87 -24
- {mcp_vector_search-0.12.0.dist-info → mcp_vector_search-0.12.1.dist-info}/RECORD +25 -23
- {mcp_vector_search-0.12.0.dist-info → mcp_vector_search-0.12.1.dist-info}/WHEEL +0 -0
- {mcp_vector_search-0.12.0.dist-info → mcp_vector_search-0.12.1.dist-info}/entry_points.txt +0 -0
- {mcp_vector_search-0.12.0.dist-info → mcp_vector_search-0.12.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
"""Install command for MCP Vector Search CLI."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
from loguru import logger
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
from rich.panel import Panel
|
|
14
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
15
|
+
|
|
16
|
+
from ...core.project import ProjectManager
|
|
17
|
+
from ..output import (
|
|
18
|
+
print_error,
|
|
19
|
+
print_info,
|
|
20
|
+
print_success,
|
|
21
|
+
print_warning,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Create console for rich output
|
|
25
|
+
console = Console()
|
|
26
|
+
|
|
27
|
+
# Create install subcommand app
|
|
28
|
+
install_app = typer.Typer(help="Install mcp-vector-search in projects")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# ============================================================================
|
|
32
|
+
# MCP Multi-Tool Integration Helpers
|
|
33
|
+
# ============================================================================
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def detect_ai_tools() -> dict[str, Path]:
|
|
37
|
+
"""Detect installed AI coding tools by checking config file existence.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Dictionary mapping tool names to their config file paths.
|
|
41
|
+
For Claude Code, returns a placeholder path since it uses project-scoped .mcp.json
|
|
42
|
+
"""
|
|
43
|
+
home = Path.home()
|
|
44
|
+
|
|
45
|
+
config_locations = {
|
|
46
|
+
"claude-desktop": home
|
|
47
|
+
/ "Library"
|
|
48
|
+
/ "Application Support"
|
|
49
|
+
/ "Claude"
|
|
50
|
+
/ "claude_desktop_config.json",
|
|
51
|
+
"cursor": home / ".cursor" / "mcp.json",
|
|
52
|
+
"windsurf": home / ".codeium" / "windsurf" / "mcp_config.json",
|
|
53
|
+
"vscode": home / ".vscode" / "mcp.json",
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Return only tools with existing config files
|
|
57
|
+
detected_tools = {}
|
|
58
|
+
for tool_name, config_path in config_locations.items():
|
|
59
|
+
if config_path.exists():
|
|
60
|
+
detected_tools[tool_name] = config_path
|
|
61
|
+
|
|
62
|
+
# Always include Claude Code as an option (it uses project-scoped .mcp.json)
|
|
63
|
+
detected_tools["claude-code"] = Path(
|
|
64
|
+
".mcp.json"
|
|
65
|
+
) # Placeholder - will be project-scoped
|
|
66
|
+
|
|
67
|
+
return detected_tools
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_mcp_server_config(
|
|
71
|
+
project_root: Path, enable_watch: bool = True, tool_name: str = ""
|
|
72
|
+
) -> dict:
|
|
73
|
+
"""Generate MCP server configuration dict.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
project_root: Path to the project root directory
|
|
77
|
+
enable_watch: Whether to enable file watching (default: True)
|
|
78
|
+
tool_name: Name of the tool (for tool-specific config adjustments)
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Dictionary containing MCP server configuration.
|
|
82
|
+
"""
|
|
83
|
+
# Base configuration
|
|
84
|
+
config = {
|
|
85
|
+
"command": "uv",
|
|
86
|
+
"args": ["run", "mcp-vector-search", "mcp"],
|
|
87
|
+
"env": {"MCP_ENABLE_FILE_WATCHING": "true" if enable_watch else "false"},
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Add "type": "stdio" for Claude Code and other tools that require it
|
|
91
|
+
if tool_name in ("claude-code", "cursor", "windsurf", "vscode"):
|
|
92
|
+
config["type"] = "stdio"
|
|
93
|
+
|
|
94
|
+
# Add cwd only for tools that support it (not Claude Code)
|
|
95
|
+
if tool_name not in ("claude-code",):
|
|
96
|
+
config["cwd"] = str(project_root.absolute())
|
|
97
|
+
|
|
98
|
+
return config
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def configure_mcp_for_tool(
|
|
102
|
+
tool_name: str,
|
|
103
|
+
config_path: Path,
|
|
104
|
+
project_root: Path,
|
|
105
|
+
server_name: str = "mcp-vector-search",
|
|
106
|
+
enable_watch: bool = True,
|
|
107
|
+
) -> bool:
|
|
108
|
+
"""Add MCP server configuration to a tool's config file.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
tool_name: Name of the AI tool (e.g., "claude-code", "cursor")
|
|
112
|
+
config_path: Path to the tool's configuration file
|
|
113
|
+
project_root: Path to the project root directory
|
|
114
|
+
server_name: Name for the MCP server entry
|
|
115
|
+
enable_watch: Whether to enable file watching
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
True if configuration was successful, False otherwise.
|
|
119
|
+
"""
|
|
120
|
+
try:
|
|
121
|
+
# For Claude Code, we create .mcp.json in project root instead of ~/.claude.json
|
|
122
|
+
if tool_name == "claude-code":
|
|
123
|
+
# Override config_path to project-scoped .mcp.json
|
|
124
|
+
config_path = project_root / ".mcp.json"
|
|
125
|
+
|
|
126
|
+
# Create backup of existing config
|
|
127
|
+
backup_path = config_path.with_suffix(config_path.suffix + ".backup")
|
|
128
|
+
|
|
129
|
+
# Load existing config or create new one
|
|
130
|
+
if config_path.exists():
|
|
131
|
+
shutil.copy2(config_path, backup_path)
|
|
132
|
+
with open(config_path) as f:
|
|
133
|
+
config = json.load(f)
|
|
134
|
+
else:
|
|
135
|
+
# Create parent directory if it doesn't exist
|
|
136
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
137
|
+
config = {}
|
|
138
|
+
|
|
139
|
+
# Ensure mcpServers section exists
|
|
140
|
+
if "mcpServers" not in config:
|
|
141
|
+
config["mcpServers"] = {}
|
|
142
|
+
|
|
143
|
+
# Get the MCP server configuration with tool-specific settings
|
|
144
|
+
server_config = get_mcp_server_config(project_root, enable_watch, tool_name)
|
|
145
|
+
|
|
146
|
+
# Add server configuration
|
|
147
|
+
config["mcpServers"][server_name] = server_config
|
|
148
|
+
|
|
149
|
+
# Write the updated config
|
|
150
|
+
with open(config_path, "w") as f:
|
|
151
|
+
json.dump(config, f, indent=2)
|
|
152
|
+
|
|
153
|
+
print_success(f" ✅ Configured {tool_name} at {config_path}")
|
|
154
|
+
return True
|
|
155
|
+
|
|
156
|
+
except Exception as e:
|
|
157
|
+
print_error(f" ❌ Failed to configure {tool_name}: {e}")
|
|
158
|
+
# Restore backup if it exists
|
|
159
|
+
if backup_path.exists():
|
|
160
|
+
shutil.copy2(backup_path, config_path)
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def setup_mcp_integration(
|
|
165
|
+
project_root: Path,
|
|
166
|
+
mcp_tool: str | None = None,
|
|
167
|
+
enable_watch: bool = True,
|
|
168
|
+
interactive: bool = True,
|
|
169
|
+
) -> dict[str, bool]:
|
|
170
|
+
"""Setup MCP integration for one or more AI tools.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
project_root: Path to the project root directory
|
|
174
|
+
mcp_tool: Specific tool to configure (None for interactive selection)
|
|
175
|
+
enable_watch: Whether to enable file watching
|
|
176
|
+
interactive: Whether to prompt user for tool selection
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Dictionary mapping tool names to success status.
|
|
180
|
+
"""
|
|
181
|
+
detected_tools = detect_ai_tools()
|
|
182
|
+
|
|
183
|
+
if not detected_tools:
|
|
184
|
+
print_warning("No AI coding tools detected on this system.")
|
|
185
|
+
print_info(
|
|
186
|
+
"Supported tools: Claude Code, Claude Desktop, Cursor, Windsurf, VS Code"
|
|
187
|
+
)
|
|
188
|
+
print_info("Install one of these tools and try again.")
|
|
189
|
+
return {}
|
|
190
|
+
|
|
191
|
+
# Determine which tools to configure
|
|
192
|
+
tools_to_configure = {}
|
|
193
|
+
|
|
194
|
+
if mcp_tool:
|
|
195
|
+
# Specific tool requested
|
|
196
|
+
if mcp_tool in detected_tools:
|
|
197
|
+
tools_to_configure[mcp_tool] = detected_tools[mcp_tool]
|
|
198
|
+
else:
|
|
199
|
+
print_error(f"Tool '{mcp_tool}' not found or not installed.")
|
|
200
|
+
print_info(f"Detected tools: {', '.join(detected_tools.keys())}")
|
|
201
|
+
return {}
|
|
202
|
+
elif interactive and len(detected_tools) > 1:
|
|
203
|
+
# Multiple tools detected, prompt user
|
|
204
|
+
console.print("\n[bold blue]🔍 Detected AI coding tools:[/bold blue]")
|
|
205
|
+
for i, tool_name in enumerate(detected_tools.keys(), 1):
|
|
206
|
+
console.print(f" {i}. {tool_name}")
|
|
207
|
+
|
|
208
|
+
console.print("\n[bold]Configure MCP integration for:[/bold]")
|
|
209
|
+
console.print(" [1] All detected tools")
|
|
210
|
+
console.print(" [2] Choose specific tool(s)")
|
|
211
|
+
console.print(" [3] Skip MCP setup")
|
|
212
|
+
|
|
213
|
+
choice = typer.prompt("\nSelect option", type=int, default=1)
|
|
214
|
+
|
|
215
|
+
if choice == 1:
|
|
216
|
+
# Configure all tools
|
|
217
|
+
tools_to_configure = detected_tools
|
|
218
|
+
elif choice == 2:
|
|
219
|
+
# Let user choose specific tools
|
|
220
|
+
console.print(
|
|
221
|
+
"\n[bold]Select tools to configure (comma-separated numbers):[/bold]"
|
|
222
|
+
)
|
|
223
|
+
tool_list = list(detected_tools.keys())
|
|
224
|
+
for i, tool_name in enumerate(tool_list, 1):
|
|
225
|
+
console.print(f" {i}. {tool_name}")
|
|
226
|
+
|
|
227
|
+
selections = typer.prompt("Tool numbers").strip()
|
|
228
|
+
for num_str in selections.split(","):
|
|
229
|
+
try:
|
|
230
|
+
idx = int(num_str.strip()) - 1
|
|
231
|
+
if 0 <= idx < len(tool_list):
|
|
232
|
+
tool_name = tool_list[idx]
|
|
233
|
+
tools_to_configure[tool_name] = detected_tools[tool_name]
|
|
234
|
+
except ValueError:
|
|
235
|
+
print_warning(f"Invalid selection: {num_str}")
|
|
236
|
+
else:
|
|
237
|
+
# Skip MCP setup
|
|
238
|
+
print_info("Skipping MCP setup")
|
|
239
|
+
return {}
|
|
240
|
+
else:
|
|
241
|
+
# Single tool or non-interactive mode - configure all
|
|
242
|
+
tools_to_configure = detected_tools
|
|
243
|
+
|
|
244
|
+
# Configure selected tools
|
|
245
|
+
results = {}
|
|
246
|
+
|
|
247
|
+
if tools_to_configure:
|
|
248
|
+
console.print("\n[bold blue]🔗 Configuring MCP integration...[/bold blue]")
|
|
249
|
+
|
|
250
|
+
for tool_name, config_path in tools_to_configure.items():
|
|
251
|
+
results[tool_name] = configure_mcp_for_tool(
|
|
252
|
+
tool_name=tool_name,
|
|
253
|
+
config_path=config_path,
|
|
254
|
+
project_root=project_root,
|
|
255
|
+
server_name="mcp-vector-search",
|
|
256
|
+
enable_watch=enable_watch,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
return results
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def print_next_steps(
|
|
263
|
+
project_root: Path,
|
|
264
|
+
indexed: bool,
|
|
265
|
+
mcp_results: dict[str, bool],
|
|
266
|
+
) -> None:
|
|
267
|
+
"""Print helpful next steps after installation.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
project_root: Path to the project root
|
|
271
|
+
indexed: Whether the codebase was indexed
|
|
272
|
+
mcp_results: Results of MCP integration (tool_name -> success)
|
|
273
|
+
"""
|
|
274
|
+
console.print("\n[bold green]🎉 Installation Complete![/bold green]")
|
|
275
|
+
|
|
276
|
+
# Show what was completed
|
|
277
|
+
console.print("\n[bold blue]✨ Setup Summary:[/bold blue]")
|
|
278
|
+
console.print(" ✅ Vector database initialized")
|
|
279
|
+
if indexed:
|
|
280
|
+
console.print(" ✅ Codebase indexed and searchable")
|
|
281
|
+
else:
|
|
282
|
+
console.print(" ⏭️ Indexing skipped (use --no-index flag)")
|
|
283
|
+
|
|
284
|
+
if mcp_results:
|
|
285
|
+
successful_tools = [tool for tool, success in mcp_results.items() if success]
|
|
286
|
+
if successful_tools:
|
|
287
|
+
console.print(
|
|
288
|
+
f" ✅ MCP integration configured for: {', '.join(successful_tools)}"
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# Next steps
|
|
292
|
+
console.print("\n[bold green]🚀 Ready to use:[/bold green]")
|
|
293
|
+
console.print(
|
|
294
|
+
" • Search your code: [code]mcp-vector-search search 'your query'[/code]"
|
|
295
|
+
)
|
|
296
|
+
console.print(" • Check status: [code]mcp-vector-search status[/code]")
|
|
297
|
+
|
|
298
|
+
if mcp_results:
|
|
299
|
+
console.print("\n[bold blue]🤖 Using MCP Integration:[/bold blue]")
|
|
300
|
+
if "claude-code" in mcp_results and mcp_results["claude-code"]:
|
|
301
|
+
console.print(" • Open Claude Code in this project directory")
|
|
302
|
+
console.print(" • Use: 'Search my code for authentication functions'")
|
|
303
|
+
if "cursor" in mcp_results and mcp_results["cursor"]:
|
|
304
|
+
console.print(" • Open Cursor in this project directory")
|
|
305
|
+
console.print(" • MCP tools should be available automatically")
|
|
306
|
+
if "claude-desktop" in mcp_results and mcp_results["claude-desktop"]:
|
|
307
|
+
console.print(" • Restart Claude Desktop")
|
|
308
|
+
console.print(" • The mcp-vector-search server will be available")
|
|
309
|
+
|
|
310
|
+
console.print(
|
|
311
|
+
"\n[dim]💡 Tip: Run 'mcp-vector-search --help' for more commands[/dim]"
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
# ============================================================================
|
|
316
|
+
# Main Install Command
|
|
317
|
+
# ============================================================================
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def main(
|
|
321
|
+
ctx: typer.Context,
|
|
322
|
+
project_path: Path = typer.Argument(
|
|
323
|
+
...,
|
|
324
|
+
help="Project directory to initialize and index",
|
|
325
|
+
),
|
|
326
|
+
extensions: str | None = typer.Option(
|
|
327
|
+
None,
|
|
328
|
+
"--extensions",
|
|
329
|
+
"-e",
|
|
330
|
+
help="Comma-separated file extensions (e.g., .py,.js,.ts,.dart)",
|
|
331
|
+
),
|
|
332
|
+
no_index: bool = typer.Option(
|
|
333
|
+
False,
|
|
334
|
+
"--no-index",
|
|
335
|
+
help="Skip initial indexing",
|
|
336
|
+
),
|
|
337
|
+
no_mcp: bool = typer.Option(
|
|
338
|
+
False,
|
|
339
|
+
"--no-mcp",
|
|
340
|
+
help="Skip MCP integration setup",
|
|
341
|
+
),
|
|
342
|
+
mcp_tool: str | None = typer.Option(
|
|
343
|
+
None,
|
|
344
|
+
"--mcp-tool",
|
|
345
|
+
help="Specific AI tool for MCP integration (claude-code, cursor, etc.)",
|
|
346
|
+
),
|
|
347
|
+
no_watch: bool = typer.Option(
|
|
348
|
+
False,
|
|
349
|
+
"--no-watch",
|
|
350
|
+
help="Disable file watching for MCP integration",
|
|
351
|
+
),
|
|
352
|
+
embedding_model: str = typer.Option(
|
|
353
|
+
"sentence-transformers/all-MiniLM-L6-v2",
|
|
354
|
+
"--embedding-model",
|
|
355
|
+
"-m",
|
|
356
|
+
help="Embedding model to use for semantic search",
|
|
357
|
+
),
|
|
358
|
+
similarity_threshold: float = typer.Option(
|
|
359
|
+
0.5,
|
|
360
|
+
"--similarity-threshold",
|
|
361
|
+
"-s",
|
|
362
|
+
help="Similarity threshold for search results (0.0 to 1.0)",
|
|
363
|
+
min=0.0,
|
|
364
|
+
max=1.0,
|
|
365
|
+
),
|
|
366
|
+
force: bool = typer.Option(
|
|
367
|
+
False,
|
|
368
|
+
"--force",
|
|
369
|
+
"-f",
|
|
370
|
+
help="Force re-installation if project is already initialized",
|
|
371
|
+
),
|
|
372
|
+
) -> None:
|
|
373
|
+
"""Install mcp-vector-search with complete setup including MCP integration.
|
|
374
|
+
|
|
375
|
+
This command provides a comprehensive one-step installation that:
|
|
376
|
+
|
|
377
|
+
✅ Initializes mcp-vector-search in the project directory
|
|
378
|
+
✅ Auto-detects programming languages and file types
|
|
379
|
+
✅ Indexes the codebase for semantic search
|
|
380
|
+
✅ Configures MCP integration for multiple AI tools
|
|
381
|
+
✅ Sets up file watching for automatic updates
|
|
382
|
+
|
|
383
|
+
Perfect for getting started quickly with semantic code search!
|
|
384
|
+
|
|
385
|
+
Examples:
|
|
386
|
+
mcp-vector-search install . # Install in current directory
|
|
387
|
+
mcp-vector-search install ~/my-project # Install in specific directory
|
|
388
|
+
mcp-vector-search install . --no-mcp # Skip MCP integration
|
|
389
|
+
mcp-vector-search install . --mcp-tool claude-code # Configure specific tool
|
|
390
|
+
mcp-vector-search install . --extensions .py,.js,.ts # Custom file types
|
|
391
|
+
mcp-vector-search install . --force # Force re-initialization
|
|
392
|
+
"""
|
|
393
|
+
try:
|
|
394
|
+
# Resolve project path
|
|
395
|
+
project_root = project_path.resolve()
|
|
396
|
+
|
|
397
|
+
# Show installation header
|
|
398
|
+
console.print(
|
|
399
|
+
Panel.fit(
|
|
400
|
+
f"[bold blue]🚀 MCP Vector Search - Complete Installation[/bold blue]\n\n"
|
|
401
|
+
f"📁 Project: [cyan]{project_root}[/cyan]\n"
|
|
402
|
+
f"🔧 Setting up with full initialization and MCP integration",
|
|
403
|
+
border_style="blue",
|
|
404
|
+
)
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
# Check if project directory exists
|
|
408
|
+
if not project_root.exists():
|
|
409
|
+
print_error(f"Project directory does not exist: {project_root}")
|
|
410
|
+
raise typer.Exit(1)
|
|
411
|
+
|
|
412
|
+
# Check if already initialized
|
|
413
|
+
project_manager = ProjectManager(project_root)
|
|
414
|
+
if project_manager.is_initialized() and not force:
|
|
415
|
+
print_success("✅ Project is already initialized!")
|
|
416
|
+
print_info("Vector search capabilities are enabled.")
|
|
417
|
+
print_info("Use --force to re-initialize if needed.")
|
|
418
|
+
|
|
419
|
+
# Show MCP configuration option
|
|
420
|
+
if not no_mcp:
|
|
421
|
+
console.print("\n[bold blue]💡 MCP Integration:[/bold blue]")
|
|
422
|
+
console.print(
|
|
423
|
+
" Run install again with --force to reconfigure MCP integration"
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
return
|
|
427
|
+
|
|
428
|
+
# Parse file extensions
|
|
429
|
+
file_extensions = None
|
|
430
|
+
if extensions:
|
|
431
|
+
file_extensions = [ext.strip() for ext in extensions.split(",")]
|
|
432
|
+
# Ensure extensions start with dot
|
|
433
|
+
file_extensions = [
|
|
434
|
+
ext if ext.startswith(".") else f".{ext}" for ext in file_extensions
|
|
435
|
+
]
|
|
436
|
+
|
|
437
|
+
# ========================================================================
|
|
438
|
+
# STEP 1: Initialize Project
|
|
439
|
+
# ========================================================================
|
|
440
|
+
with Progress(
|
|
441
|
+
SpinnerColumn(),
|
|
442
|
+
TextColumn("[progress.description]{task.description}"),
|
|
443
|
+
console=console,
|
|
444
|
+
) as progress:
|
|
445
|
+
task = progress.add_task("📁 Initializing project...", total=None)
|
|
446
|
+
|
|
447
|
+
# Initialize the project
|
|
448
|
+
project_manager.initialize(
|
|
449
|
+
file_extensions=file_extensions,
|
|
450
|
+
embedding_model=embedding_model,
|
|
451
|
+
similarity_threshold=similarity_threshold,
|
|
452
|
+
force=force,
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
progress.update(task, completed=True)
|
|
456
|
+
print_success("✅ Project initialized successfully")
|
|
457
|
+
|
|
458
|
+
# ========================================================================
|
|
459
|
+
# STEP 2: Index Codebase (unless --no-index)
|
|
460
|
+
# ========================================================================
|
|
461
|
+
indexed = False
|
|
462
|
+
if not no_index:
|
|
463
|
+
with Progress(
|
|
464
|
+
SpinnerColumn(),
|
|
465
|
+
TextColumn("[progress.description]{task.description}"),
|
|
466
|
+
console=console,
|
|
467
|
+
) as progress:
|
|
468
|
+
task = progress.add_task("🔍 Indexing codebase...", total=None)
|
|
469
|
+
|
|
470
|
+
# Import and run indexing
|
|
471
|
+
from .index import run_indexing
|
|
472
|
+
|
|
473
|
+
try:
|
|
474
|
+
asyncio.run(
|
|
475
|
+
run_indexing(
|
|
476
|
+
project_root=project_root,
|
|
477
|
+
force_reindex=False,
|
|
478
|
+
show_progress=False, # We handle progress here
|
|
479
|
+
)
|
|
480
|
+
)
|
|
481
|
+
indexed = True
|
|
482
|
+
progress.update(task, completed=True)
|
|
483
|
+
print_success("✅ Codebase indexed successfully")
|
|
484
|
+
except Exception as e:
|
|
485
|
+
print_error(f"❌ Indexing failed: {e}")
|
|
486
|
+
print_info("You can run 'mcp-vector-search index' later")
|
|
487
|
+
else:
|
|
488
|
+
print_info("⏭️ Indexing skipped (--no-index)")
|
|
489
|
+
|
|
490
|
+
# ========================================================================
|
|
491
|
+
# STEP 3: Configure MCP Integration (unless --no-mcp)
|
|
492
|
+
# ========================================================================
|
|
493
|
+
mcp_results = {}
|
|
494
|
+
if not no_mcp:
|
|
495
|
+
enable_watch = not no_watch
|
|
496
|
+
mcp_results = setup_mcp_integration(
|
|
497
|
+
project_root=project_root,
|
|
498
|
+
mcp_tool=mcp_tool,
|
|
499
|
+
enable_watch=enable_watch,
|
|
500
|
+
interactive=True, # Allow interactive tool selection
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
if not mcp_results:
|
|
504
|
+
print_info("⏭️ MCP integration skipped")
|
|
505
|
+
else:
|
|
506
|
+
print_info("⏭️ MCP integration skipped (--no-mcp)")
|
|
507
|
+
|
|
508
|
+
# ========================================================================
|
|
509
|
+
# STEP 4: Verification
|
|
510
|
+
# ========================================================================
|
|
511
|
+
console.print("\n[bold blue]✅ Verifying installation...[/bold blue]")
|
|
512
|
+
|
|
513
|
+
# Check project initialized
|
|
514
|
+
if project_manager.is_initialized():
|
|
515
|
+
print_success(" ✅ Project configuration created")
|
|
516
|
+
|
|
517
|
+
# Check index created
|
|
518
|
+
if indexed:
|
|
519
|
+
print_success(" ✅ Index created and populated")
|
|
520
|
+
|
|
521
|
+
# Check MCP configured
|
|
522
|
+
if mcp_results:
|
|
523
|
+
successful_tools = [
|
|
524
|
+
tool for tool, success in mcp_results.items() if success
|
|
525
|
+
]
|
|
526
|
+
if successful_tools:
|
|
527
|
+
print_success(f" ✅ MCP configured for: {', '.join(successful_tools)}")
|
|
528
|
+
|
|
529
|
+
# ========================================================================
|
|
530
|
+
# STEP 5: Print Next Steps
|
|
531
|
+
# ========================================================================
|
|
532
|
+
print_next_steps(
|
|
533
|
+
project_root=project_root,
|
|
534
|
+
indexed=indexed,
|
|
535
|
+
mcp_results=mcp_results,
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
except Exception as e:
|
|
539
|
+
logger.error(f"Installation failed: {e}")
|
|
540
|
+
print_error(f"❌ Installation failed: {e}")
|
|
541
|
+
|
|
542
|
+
# Provide recovery instructions
|
|
543
|
+
console.print("\n[bold]Recovery steps:[/bold]")
|
|
544
|
+
console.print(" 1. Check that the project directory exists and is writable")
|
|
545
|
+
console.print(
|
|
546
|
+
" 2. Ensure required dependencies are installed: [code]pip install mcp-vector-search[/code]"
|
|
547
|
+
)
|
|
548
|
+
console.print(
|
|
549
|
+
" 3. Try running with --force to override existing configuration"
|
|
550
|
+
)
|
|
551
|
+
console.print(" 4. Check logs with --verbose flag for more details")
|
|
552
|
+
|
|
553
|
+
raise typer.Exit(1)
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
@install_app.command("demo")
|
|
557
|
+
def demo(
|
|
558
|
+
project_root: Path | None = typer.Option(
|
|
559
|
+
None,
|
|
560
|
+
"--project-root",
|
|
561
|
+
"-p",
|
|
562
|
+
help="Project root directory (auto-detected if not specified)",
|
|
563
|
+
exists=True,
|
|
564
|
+
file_okay=False,
|
|
565
|
+
dir_okay=True,
|
|
566
|
+
readable=True,
|
|
567
|
+
),
|
|
568
|
+
) -> None:
|
|
569
|
+
"""Run installation demo with sample project."""
|
|
570
|
+
try:
|
|
571
|
+
import tempfile
|
|
572
|
+
|
|
573
|
+
print_info("🎬 Running mcp-vector-search installation demo...")
|
|
574
|
+
|
|
575
|
+
# Create temporary demo directory
|
|
576
|
+
with tempfile.TemporaryDirectory(prefix="mcp-demo-") as temp_dir:
|
|
577
|
+
demo_dir = Path(temp_dir) / "demo-project"
|
|
578
|
+
demo_dir.mkdir()
|
|
579
|
+
|
|
580
|
+
# Create sample files
|
|
581
|
+
(demo_dir / "main.py").write_text("""
|
|
582
|
+
def main():
|
|
583
|
+
'''Main entry point for the application.'''
|
|
584
|
+
print("Hello, World!")
|
|
585
|
+
user_service = UserService()
|
|
586
|
+
user_service.create_user("Alice", "alice@example.com")
|
|
587
|
+
|
|
588
|
+
class UserService:
|
|
589
|
+
'''Service for managing users.'''
|
|
590
|
+
|
|
591
|
+
def create_user(self, name: str, email: str):
|
|
592
|
+
'''Create a new user with the given name and email.'''
|
|
593
|
+
print(f"Creating user: {name} ({email})")
|
|
594
|
+
return {"name": name, "email": email}
|
|
595
|
+
|
|
596
|
+
def authenticate_user(self, email: str, password: str):
|
|
597
|
+
'''Authenticate user with email and password.'''
|
|
598
|
+
# Simple authentication logic
|
|
599
|
+
return email.endswith("@example.com")
|
|
600
|
+
|
|
601
|
+
if __name__ == "__main__":
|
|
602
|
+
main()
|
|
603
|
+
""")
|
|
604
|
+
|
|
605
|
+
(demo_dir / "utils.py").write_text("""
|
|
606
|
+
import json
|
|
607
|
+
from typing import Dict, Any
|
|
608
|
+
|
|
609
|
+
def load_config(config_path: str) -> Dict[str, Any]:
|
|
610
|
+
'''Load configuration from JSON file.'''
|
|
611
|
+
with open(config_path, 'r') as f:
|
|
612
|
+
return json.load(f)
|
|
613
|
+
|
|
614
|
+
def validate_email(email: str) -> bool:
|
|
615
|
+
'''Validate email address format.'''
|
|
616
|
+
return "@" in email and "." in email.split("@")[1]
|
|
617
|
+
|
|
618
|
+
def hash_password(password: str) -> str:
|
|
619
|
+
'''Hash password for secure storage.'''
|
|
620
|
+
import hashlib
|
|
621
|
+
return hashlib.sha256(password.encode()).hexdigest()
|
|
622
|
+
""")
|
|
623
|
+
|
|
624
|
+
console.print(
|
|
625
|
+
f"\n[bold blue]📁 Created demo project at:[/bold blue] {demo_dir}"
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
# Run installation
|
|
629
|
+
print_info("Installing mcp-vector-search in demo project...")
|
|
630
|
+
|
|
631
|
+
# Use subprocess to run the install command
|
|
632
|
+
result = subprocess.run(
|
|
633
|
+
[
|
|
634
|
+
sys.executable,
|
|
635
|
+
"-m",
|
|
636
|
+
"mcp_vector_search.cli.main",
|
|
637
|
+
"--project-root",
|
|
638
|
+
str(demo_dir),
|
|
639
|
+
"install",
|
|
640
|
+
str(demo_dir),
|
|
641
|
+
"--extensions",
|
|
642
|
+
".py",
|
|
643
|
+
"--no-mcp", # Skip MCP for demo
|
|
644
|
+
],
|
|
645
|
+
capture_output=True,
|
|
646
|
+
text=True,
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
if result.returncode == 0:
|
|
650
|
+
print_success("✅ Demo installation completed!")
|
|
651
|
+
|
|
652
|
+
# Run a sample search
|
|
653
|
+
print_info("Running sample search: 'user authentication'...")
|
|
654
|
+
|
|
655
|
+
search_result = subprocess.run(
|
|
656
|
+
[
|
|
657
|
+
sys.executable,
|
|
658
|
+
"-m",
|
|
659
|
+
"mcp_vector_search.cli.main",
|
|
660
|
+
"--project-root",
|
|
661
|
+
str(demo_dir),
|
|
662
|
+
"search",
|
|
663
|
+
"user authentication",
|
|
664
|
+
"--limit",
|
|
665
|
+
"3",
|
|
666
|
+
],
|
|
667
|
+
capture_output=True,
|
|
668
|
+
text=True,
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
if search_result.returncode == 0:
|
|
672
|
+
console.print(
|
|
673
|
+
"\n[bold green]🔍 Sample search results:[/bold green]"
|
|
674
|
+
)
|
|
675
|
+
console.print(search_result.stdout)
|
|
676
|
+
else:
|
|
677
|
+
print_warning("Search demo failed, but installation was successful")
|
|
678
|
+
|
|
679
|
+
console.print("\n[bold blue]🎉 Demo completed![/bold blue]")
|
|
680
|
+
console.print(f"Demo project was created at: [cyan]{demo_dir}[/cyan]")
|
|
681
|
+
console.print(
|
|
682
|
+
"The temporary directory will be cleaned up automatically."
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
else:
|
|
686
|
+
print_error(f"Demo installation failed: {result.stderr}")
|
|
687
|
+
raise typer.Exit(1)
|
|
688
|
+
|
|
689
|
+
except Exception as e:
|
|
690
|
+
logger.error(f"Demo failed: {e}")
|
|
691
|
+
print_error(f"Demo failed: {e}")
|
|
692
|
+
raise typer.Exit(1)
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
if __name__ == "__main__":
|
|
696
|
+
install_app()
|