mcp-vector-search 0.0.3__py3-none-any.whl → 0.4.11__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 +3 -2
- mcp_vector_search/cli/commands/auto_index.py +397 -0
- mcp_vector_search/cli/commands/config.py +88 -40
- mcp_vector_search/cli/commands/index.py +198 -52
- mcp_vector_search/cli/commands/init.py +472 -58
- mcp_vector_search/cli/commands/install.py +284 -0
- mcp_vector_search/cli/commands/mcp.py +495 -0
- mcp_vector_search/cli/commands/search.py +241 -87
- mcp_vector_search/cli/commands/status.py +184 -58
- mcp_vector_search/cli/commands/watch.py +34 -35
- mcp_vector_search/cli/didyoumean.py +184 -0
- mcp_vector_search/cli/export.py +320 -0
- mcp_vector_search/cli/history.py +292 -0
- mcp_vector_search/cli/interactive.py +342 -0
- mcp_vector_search/cli/main.py +163 -26
- mcp_vector_search/cli/output.py +63 -45
- mcp_vector_search/config/defaults.py +50 -36
- mcp_vector_search/config/settings.py +49 -35
- mcp_vector_search/core/auto_indexer.py +298 -0
- mcp_vector_search/core/connection_pool.py +322 -0
- mcp_vector_search/core/database.py +335 -25
- mcp_vector_search/core/embeddings.py +73 -29
- mcp_vector_search/core/exceptions.py +19 -2
- mcp_vector_search/core/factory.py +310 -0
- mcp_vector_search/core/git_hooks.py +345 -0
- mcp_vector_search/core/indexer.py +237 -73
- mcp_vector_search/core/models.py +21 -19
- mcp_vector_search/core/project.py +73 -58
- mcp_vector_search/core/scheduler.py +330 -0
- mcp_vector_search/core/search.py +574 -86
- mcp_vector_search/core/watcher.py +48 -46
- mcp_vector_search/mcp/__init__.py +4 -0
- mcp_vector_search/mcp/__main__.py +25 -0
- mcp_vector_search/mcp/server.py +701 -0
- mcp_vector_search/parsers/base.py +30 -31
- mcp_vector_search/parsers/javascript.py +74 -48
- mcp_vector_search/parsers/python.py +57 -49
- mcp_vector_search/parsers/registry.py +47 -32
- mcp_vector_search/parsers/text.py +179 -0
- mcp_vector_search/utils/__init__.py +40 -0
- mcp_vector_search/utils/gitignore.py +229 -0
- mcp_vector_search/utils/timing.py +334 -0
- mcp_vector_search/utils/version.py +47 -0
- {mcp_vector_search-0.0.3.dist-info → mcp_vector_search-0.4.11.dist-info}/METADATA +173 -7
- mcp_vector_search-0.4.11.dist-info/RECORD +54 -0
- mcp_vector_search-0.0.3.dist-info/RECORD +0 -35
- {mcp_vector_search-0.0.3.dist-info → mcp_vector_search-0.4.11.dist-info}/WHEEL +0 -0
- {mcp_vector_search-0.0.3.dist-info → mcp_vector_search-0.4.11.dist-info}/entry_points.txt +0 -0
- {mcp_vector_search-0.0.3.dist-info → mcp_vector_search-0.4.11.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import List, Optional
|
|
6
5
|
|
|
7
6
|
import typer
|
|
8
7
|
from loguru import logger
|
|
@@ -12,22 +11,29 @@ from ...core.embeddings import create_embedding_function
|
|
|
12
11
|
from ...core.exceptions import ProjectNotFoundError
|
|
13
12
|
from ...core.project import ProjectManager
|
|
14
13
|
from ...core.search import SemanticSearchEngine
|
|
14
|
+
from ..didyoumean import create_enhanced_typer
|
|
15
15
|
from ..output import (
|
|
16
|
-
console,
|
|
17
16
|
print_error,
|
|
18
|
-
print_info,
|
|
19
17
|
print_search_results,
|
|
20
|
-
print_warning,
|
|
21
18
|
)
|
|
22
19
|
|
|
23
|
-
# Create search subcommand app
|
|
24
|
-
search_app =
|
|
20
|
+
# Create search subcommand app with "did you mean" functionality (kept for backward compatibility)
|
|
21
|
+
search_app = create_enhanced_typer(help="Search code semantically")
|
|
25
22
|
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
def main(
|
|
24
|
+
def search_main(
|
|
29
25
|
ctx: typer.Context,
|
|
30
26
|
query: str = typer.Argument(..., help="Search query"),
|
|
27
|
+
project_root: Path | None = typer.Option(
|
|
28
|
+
None,
|
|
29
|
+
"--project-root",
|
|
30
|
+
"-p",
|
|
31
|
+
help="Project root directory (auto-detected if not specified)",
|
|
32
|
+
exists=True,
|
|
33
|
+
file_okay=False,
|
|
34
|
+
dir_okay=True,
|
|
35
|
+
readable=True,
|
|
36
|
+
),
|
|
31
37
|
limit: int = typer.Option(
|
|
32
38
|
10,
|
|
33
39
|
"--limit",
|
|
@@ -36,28 +42,28 @@ def main(
|
|
|
36
42
|
min=1,
|
|
37
43
|
max=100,
|
|
38
44
|
),
|
|
39
|
-
files:
|
|
45
|
+
files: str | None = typer.Option(
|
|
40
46
|
None,
|
|
41
47
|
"--files",
|
|
42
48
|
"-f",
|
|
43
49
|
help="Filter by file patterns (e.g., '*.py' or 'src/*.js')",
|
|
44
50
|
),
|
|
45
|
-
language:
|
|
51
|
+
language: str | None = typer.Option(
|
|
46
52
|
None,
|
|
47
53
|
"--language",
|
|
48
54
|
help="Filter by programming language",
|
|
49
55
|
),
|
|
50
|
-
function_name:
|
|
56
|
+
function_name: str | None = typer.Option(
|
|
51
57
|
None,
|
|
52
58
|
"--function",
|
|
53
59
|
help="Filter by function name",
|
|
54
60
|
),
|
|
55
|
-
class_name:
|
|
61
|
+
class_name: str | None = typer.Option(
|
|
56
62
|
None,
|
|
57
63
|
"--class",
|
|
58
64
|
help="Filter by class name",
|
|
59
65
|
),
|
|
60
|
-
similarity_threshold:
|
|
66
|
+
similarity_threshold: float | None = typer.Option(
|
|
61
67
|
None,
|
|
62
68
|
"--threshold",
|
|
63
69
|
"-t",
|
|
@@ -65,6 +71,21 @@ def main(
|
|
|
65
71
|
min=0.0,
|
|
66
72
|
max=1.0,
|
|
67
73
|
),
|
|
74
|
+
similar: bool = typer.Option(
|
|
75
|
+
False,
|
|
76
|
+
"--similar",
|
|
77
|
+
help="Find code similar to the query (treats query as file path)",
|
|
78
|
+
),
|
|
79
|
+
context: bool = typer.Option(
|
|
80
|
+
False,
|
|
81
|
+
"--context",
|
|
82
|
+
help="Search for code based on contextual description",
|
|
83
|
+
),
|
|
84
|
+
focus: str | None = typer.Option(
|
|
85
|
+
None,
|
|
86
|
+
"--focus",
|
|
87
|
+
help="Focus areas for context search (comma-separated)",
|
|
88
|
+
),
|
|
68
89
|
no_content: bool = typer.Option(
|
|
69
90
|
False,
|
|
70
91
|
"--no-content",
|
|
@@ -75,35 +96,92 @@ def main(
|
|
|
75
96
|
"--json",
|
|
76
97
|
help="Output results in JSON format",
|
|
77
98
|
),
|
|
99
|
+
export_format: str | None = typer.Option(
|
|
100
|
+
None,
|
|
101
|
+
"--export",
|
|
102
|
+
help="Export results to file (json, csv, markdown, summary)",
|
|
103
|
+
),
|
|
104
|
+
export_path: Path | None = typer.Option(
|
|
105
|
+
None,
|
|
106
|
+
"--export-path",
|
|
107
|
+
help="Custom export file path",
|
|
108
|
+
),
|
|
78
109
|
) -> None:
|
|
79
110
|
"""Search your codebase semantically.
|
|
80
|
-
|
|
111
|
+
|
|
81
112
|
This command performs semantic search across your indexed codebase,
|
|
82
113
|
finding code that is conceptually similar to your query even if it
|
|
83
114
|
doesn't contain the exact keywords.
|
|
84
|
-
|
|
115
|
+
|
|
85
116
|
Examples:
|
|
86
117
|
mcp-vector-search search "authentication middleware"
|
|
87
118
|
mcp-vector-search search "database connection" --language python
|
|
88
119
|
mcp-vector-search search "error handling" --files "*.js" --limit 5
|
|
89
120
|
mcp-vector-search search "user validation" --function validate --json
|
|
121
|
+
mcp-vector-search search "src/auth.py" --similar
|
|
122
|
+
mcp-vector-search search "implement rate limiting" --context
|
|
123
|
+
mcp-vector-search search "user authentication" --context --focus security,middleware
|
|
90
124
|
"""
|
|
91
125
|
try:
|
|
92
|
-
project_root = ctx.obj.get("project_root") or Path.cwd()
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
126
|
+
project_root = project_root or ctx.obj.get("project_root") or Path.cwd()
|
|
127
|
+
|
|
128
|
+
# Validate mutually exclusive options
|
|
129
|
+
if similar and context:
|
|
130
|
+
print_error("Cannot use both --similar and --context flags together")
|
|
131
|
+
raise typer.Exit(1)
|
|
132
|
+
|
|
133
|
+
# Route to appropriate search function
|
|
134
|
+
if similar:
|
|
135
|
+
# Similar search - treat query as file path
|
|
136
|
+
file_path = Path(query)
|
|
137
|
+
if not file_path.exists():
|
|
138
|
+
print_error(f"File not found: {query}")
|
|
139
|
+
raise typer.Exit(1)
|
|
140
|
+
|
|
141
|
+
asyncio.run(
|
|
142
|
+
run_similar_search(
|
|
143
|
+
project_root=project_root,
|
|
144
|
+
file_path=file_path,
|
|
145
|
+
function_name=function_name,
|
|
146
|
+
limit=limit,
|
|
147
|
+
similarity_threshold=similarity_threshold,
|
|
148
|
+
json_output=json_output,
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
elif context:
|
|
152
|
+
# Context search
|
|
153
|
+
focus_areas = None
|
|
154
|
+
if focus:
|
|
155
|
+
focus_areas = [area.strip() for area in focus.split(",")]
|
|
156
|
+
|
|
157
|
+
asyncio.run(
|
|
158
|
+
run_context_search(
|
|
159
|
+
project_root=project_root,
|
|
160
|
+
description=query,
|
|
161
|
+
focus_areas=focus_areas,
|
|
162
|
+
limit=limit,
|
|
163
|
+
json_output=json_output,
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
# Default semantic search
|
|
168
|
+
asyncio.run(
|
|
169
|
+
run_search(
|
|
170
|
+
project_root=project_root,
|
|
171
|
+
query=query,
|
|
172
|
+
limit=limit,
|
|
173
|
+
files=files,
|
|
174
|
+
language=language,
|
|
175
|
+
function_name=function_name,
|
|
176
|
+
class_name=class_name,
|
|
177
|
+
similarity_threshold=similarity_threshold,
|
|
178
|
+
show_content=not no_content,
|
|
179
|
+
json_output=json_output,
|
|
180
|
+
export_format=export_format,
|
|
181
|
+
export_path=export_path,
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
|
|
107
185
|
except Exception as e:
|
|
108
186
|
logger.error(f"Search failed: {e}")
|
|
109
187
|
print_error(f"Search failed: {e}")
|
|
@@ -114,38 +192,40 @@ async def run_search(
|
|
|
114
192
|
project_root: Path,
|
|
115
193
|
query: str,
|
|
116
194
|
limit: int = 10,
|
|
117
|
-
files:
|
|
118
|
-
language:
|
|
119
|
-
function_name:
|
|
120
|
-
class_name:
|
|
121
|
-
similarity_threshold:
|
|
195
|
+
files: str | None = None,
|
|
196
|
+
language: str | None = None,
|
|
197
|
+
function_name: str | None = None,
|
|
198
|
+
class_name: str | None = None,
|
|
199
|
+
similarity_threshold: float | None = None,
|
|
122
200
|
show_content: bool = True,
|
|
123
201
|
json_output: bool = False,
|
|
202
|
+
export_format: str | None = None,
|
|
203
|
+
export_path: Path | None = None,
|
|
124
204
|
) -> None:
|
|
125
205
|
"""Run semantic search."""
|
|
126
206
|
# Load project configuration
|
|
127
207
|
project_manager = ProjectManager(project_root)
|
|
128
|
-
|
|
208
|
+
|
|
129
209
|
if not project_manager.is_initialized():
|
|
130
210
|
raise ProjectNotFoundError(
|
|
131
211
|
f"Project not initialized at {project_root}. Run 'mcp-vector-search init' first."
|
|
132
212
|
)
|
|
133
|
-
|
|
213
|
+
|
|
134
214
|
config = project_manager.load_config()
|
|
135
|
-
|
|
215
|
+
|
|
136
216
|
# Setup database and search engine
|
|
137
217
|
embedding_function, _ = create_embedding_function(config.embedding_model)
|
|
138
218
|
database = ChromaVectorDatabase(
|
|
139
219
|
persist_directory=config.index_path,
|
|
140
220
|
embedding_function=embedding_function,
|
|
141
221
|
)
|
|
142
|
-
|
|
222
|
+
|
|
143
223
|
search_engine = SemanticSearchEngine(
|
|
144
224
|
database=database,
|
|
145
225
|
project_root=project_root,
|
|
146
226
|
similarity_threshold=similarity_threshold or config.similarity_threshold,
|
|
147
227
|
)
|
|
148
|
-
|
|
228
|
+
|
|
149
229
|
# Build filters
|
|
150
230
|
filters = {}
|
|
151
231
|
if language:
|
|
@@ -157,7 +237,7 @@ async def run_search(
|
|
|
157
237
|
if files:
|
|
158
238
|
# Simple file pattern matching (could be enhanced)
|
|
159
239
|
filters["file_path"] = files
|
|
160
|
-
|
|
240
|
+
|
|
161
241
|
try:
|
|
162
242
|
async with database:
|
|
163
243
|
results = await search_engine.search(
|
|
@@ -167,9 +247,53 @@ async def run_search(
|
|
|
167
247
|
similarity_threshold=similarity_threshold,
|
|
168
248
|
include_context=show_content,
|
|
169
249
|
)
|
|
170
|
-
|
|
250
|
+
|
|
251
|
+
# Handle export if requested
|
|
252
|
+
if export_format:
|
|
253
|
+
from ..export import SearchResultExporter, get_export_path
|
|
254
|
+
|
|
255
|
+
exporter = SearchResultExporter()
|
|
256
|
+
|
|
257
|
+
# Determine export path
|
|
258
|
+
if export_path:
|
|
259
|
+
output_path = export_path
|
|
260
|
+
else:
|
|
261
|
+
output_path = get_export_path(export_format, query, project_root)
|
|
262
|
+
|
|
263
|
+
# Export based on format
|
|
264
|
+
success = False
|
|
265
|
+
if export_format == "json":
|
|
266
|
+
success = exporter.export_to_json(results, output_path, query)
|
|
267
|
+
elif export_format == "csv":
|
|
268
|
+
success = exporter.export_to_csv(results, output_path, query)
|
|
269
|
+
elif export_format == "markdown":
|
|
270
|
+
success = exporter.export_to_markdown(
|
|
271
|
+
results, output_path, query, show_content
|
|
272
|
+
)
|
|
273
|
+
elif export_format == "summary":
|
|
274
|
+
success = exporter.export_summary_table(results, output_path, query)
|
|
275
|
+
else:
|
|
276
|
+
from ..output import print_error
|
|
277
|
+
|
|
278
|
+
print_error(f"Unsupported export format: {export_format}")
|
|
279
|
+
|
|
280
|
+
if not success:
|
|
281
|
+
return
|
|
282
|
+
|
|
283
|
+
# Save to search history
|
|
284
|
+
from ..history import SearchHistory
|
|
285
|
+
|
|
286
|
+
history_manager = SearchHistory(project_root)
|
|
287
|
+
history_manager.add_search(
|
|
288
|
+
query=query,
|
|
289
|
+
results_count=len(results),
|
|
290
|
+
filters=filters if filters else None,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# Display results
|
|
171
294
|
if json_output:
|
|
172
295
|
from ..output import print_json
|
|
296
|
+
|
|
173
297
|
results_data = [result.to_dict() for result in results]
|
|
174
298
|
print_json(results_data, title="Search Results")
|
|
175
299
|
else:
|
|
@@ -178,14 +302,13 @@ async def run_search(
|
|
|
178
302
|
query=query,
|
|
179
303
|
show_content=show_content,
|
|
180
304
|
)
|
|
181
|
-
|
|
305
|
+
|
|
182
306
|
except Exception as e:
|
|
183
307
|
logger.error(f"Search execution failed: {e}")
|
|
184
308
|
raise
|
|
185
309
|
|
|
186
310
|
|
|
187
|
-
|
|
188
|
-
def search_similar(
|
|
311
|
+
def search_similar_cmd(
|
|
189
312
|
ctx: typer.Context,
|
|
190
313
|
file_path: Path = typer.Argument(
|
|
191
314
|
...,
|
|
@@ -195,7 +318,17 @@ def search_similar(
|
|
|
195
318
|
dir_okay=False,
|
|
196
319
|
readable=True,
|
|
197
320
|
),
|
|
198
|
-
|
|
321
|
+
project_root: Path | None = typer.Option(
|
|
322
|
+
None,
|
|
323
|
+
"--project-root",
|
|
324
|
+
"-p",
|
|
325
|
+
help="Project root directory (auto-detected if not specified)",
|
|
326
|
+
exists=True,
|
|
327
|
+
file_okay=False,
|
|
328
|
+
dir_okay=True,
|
|
329
|
+
readable=True,
|
|
330
|
+
),
|
|
331
|
+
function_name: str | None = typer.Option(
|
|
199
332
|
None,
|
|
200
333
|
"--function",
|
|
201
334
|
"-f",
|
|
@@ -209,7 +342,7 @@ def search_similar(
|
|
|
209
342
|
min=1,
|
|
210
343
|
max=100,
|
|
211
344
|
),
|
|
212
|
-
similarity_threshold:
|
|
345
|
+
similarity_threshold: float | None = typer.Option(
|
|
213
346
|
None,
|
|
214
347
|
"--threshold",
|
|
215
348
|
"-t",
|
|
@@ -224,23 +357,25 @@ def search_similar(
|
|
|
224
357
|
),
|
|
225
358
|
) -> None:
|
|
226
359
|
"""Find code similar to a specific file or function.
|
|
227
|
-
|
|
360
|
+
|
|
228
361
|
Examples:
|
|
229
362
|
mcp-vector-search search similar src/auth.py
|
|
230
363
|
mcp-vector-search search similar src/utils.py --function validate_email
|
|
231
364
|
"""
|
|
232
365
|
try:
|
|
233
|
-
project_root = ctx.obj.get("project_root") or Path.cwd()
|
|
234
|
-
|
|
235
|
-
asyncio.run(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
366
|
+
project_root = project_root or ctx.obj.get("project_root") or Path.cwd()
|
|
367
|
+
|
|
368
|
+
asyncio.run(
|
|
369
|
+
run_similar_search(
|
|
370
|
+
project_root=project_root,
|
|
371
|
+
file_path=file_path,
|
|
372
|
+
function_name=function_name,
|
|
373
|
+
limit=limit,
|
|
374
|
+
similarity_threshold=similarity_threshold,
|
|
375
|
+
json_output=json_output,
|
|
376
|
+
)
|
|
377
|
+
)
|
|
378
|
+
|
|
244
379
|
except Exception as e:
|
|
245
380
|
logger.error(f"Similar search failed: {e}")
|
|
246
381
|
print_error(f"Similar search failed: {e}")
|
|
@@ -250,27 +385,27 @@ def search_similar(
|
|
|
250
385
|
async def run_similar_search(
|
|
251
386
|
project_root: Path,
|
|
252
387
|
file_path: Path,
|
|
253
|
-
function_name:
|
|
388
|
+
function_name: str | None = None,
|
|
254
389
|
limit: int = 10,
|
|
255
|
-
similarity_threshold:
|
|
390
|
+
similarity_threshold: float | None = None,
|
|
256
391
|
json_output: bool = False,
|
|
257
392
|
) -> None:
|
|
258
393
|
"""Run similar code search."""
|
|
259
394
|
project_manager = ProjectManager(project_root)
|
|
260
395
|
config = project_manager.load_config()
|
|
261
|
-
|
|
396
|
+
|
|
262
397
|
embedding_function, _ = create_embedding_function(config.embedding_model)
|
|
263
398
|
database = ChromaVectorDatabase(
|
|
264
399
|
persist_directory=config.index_path,
|
|
265
400
|
embedding_function=embedding_function,
|
|
266
401
|
)
|
|
267
|
-
|
|
402
|
+
|
|
268
403
|
search_engine = SemanticSearchEngine(
|
|
269
404
|
database=database,
|
|
270
405
|
project_root=project_root,
|
|
271
406
|
similarity_threshold=similarity_threshold or config.similarity_threshold,
|
|
272
407
|
)
|
|
273
|
-
|
|
408
|
+
|
|
274
409
|
async with database:
|
|
275
410
|
results = await search_engine.search_similar(
|
|
276
411
|
file_path=file_path,
|
|
@@ -278,16 +413,17 @@ async def run_similar_search(
|
|
|
278
413
|
limit=limit,
|
|
279
414
|
similarity_threshold=similarity_threshold,
|
|
280
415
|
)
|
|
281
|
-
|
|
416
|
+
|
|
282
417
|
if json_output:
|
|
283
418
|
from ..output import print_json
|
|
419
|
+
|
|
284
420
|
results_data = [result.to_dict() for result in results]
|
|
285
421
|
print_json(results_data, title="Similar Code Results")
|
|
286
422
|
else:
|
|
287
423
|
query_desc = f"{file_path}"
|
|
288
424
|
if function_name:
|
|
289
425
|
query_desc += f" → {function_name}()"
|
|
290
|
-
|
|
426
|
+
|
|
291
427
|
print_search_results(
|
|
292
428
|
results=results,
|
|
293
429
|
query=f"Similar to: {query_desc}",
|
|
@@ -295,11 +431,20 @@ async def run_similar_search(
|
|
|
295
431
|
)
|
|
296
432
|
|
|
297
433
|
|
|
298
|
-
|
|
299
|
-
def search_context(
|
|
434
|
+
def search_context_cmd(
|
|
300
435
|
ctx: typer.Context,
|
|
301
436
|
description: str = typer.Argument(..., help="Context description"),
|
|
302
|
-
|
|
437
|
+
project_root: Path | None = typer.Option(
|
|
438
|
+
None,
|
|
439
|
+
"--project-root",
|
|
440
|
+
"-p",
|
|
441
|
+
help="Project root directory (auto-detected if not specified)",
|
|
442
|
+
exists=True,
|
|
443
|
+
file_okay=False,
|
|
444
|
+
dir_okay=True,
|
|
445
|
+
readable=True,
|
|
446
|
+
),
|
|
447
|
+
focus: str | None = typer.Option(
|
|
303
448
|
None,
|
|
304
449
|
"--focus",
|
|
305
450
|
help="Comma-separated focus areas (e.g., 'security,authentication')",
|
|
@@ -319,26 +464,28 @@ def search_context(
|
|
|
319
464
|
),
|
|
320
465
|
) -> None:
|
|
321
466
|
"""Search for code based on contextual description.
|
|
322
|
-
|
|
467
|
+
|
|
323
468
|
Examples:
|
|
324
469
|
mcp-vector-search search context "implement rate limiting"
|
|
325
470
|
mcp-vector-search search context "user authentication" --focus security,middleware
|
|
326
471
|
"""
|
|
327
472
|
try:
|
|
328
|
-
project_root = ctx.obj.get("project_root") or Path.cwd()
|
|
329
|
-
|
|
473
|
+
project_root = project_root or ctx.obj.get("project_root") or Path.cwd()
|
|
474
|
+
|
|
330
475
|
focus_areas = None
|
|
331
476
|
if focus:
|
|
332
477
|
focus_areas = [area.strip() for area in focus.split(",")]
|
|
333
|
-
|
|
334
|
-
asyncio.run(
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
478
|
+
|
|
479
|
+
asyncio.run(
|
|
480
|
+
run_context_search(
|
|
481
|
+
project_root=project_root,
|
|
482
|
+
description=description,
|
|
483
|
+
focus_areas=focus_areas,
|
|
484
|
+
limit=limit,
|
|
485
|
+
json_output=json_output,
|
|
486
|
+
)
|
|
487
|
+
)
|
|
488
|
+
|
|
342
489
|
except Exception as e:
|
|
343
490
|
logger.error(f"Context search failed: {e}")
|
|
344
491
|
print_error(f"Context search failed: {e}")
|
|
@@ -348,42 +495,43 @@ def search_context(
|
|
|
348
495
|
async def run_context_search(
|
|
349
496
|
project_root: Path,
|
|
350
497
|
description: str,
|
|
351
|
-
focus_areas:
|
|
498
|
+
focus_areas: list[str] | None = None,
|
|
352
499
|
limit: int = 10,
|
|
353
500
|
json_output: bool = False,
|
|
354
501
|
) -> None:
|
|
355
502
|
"""Run contextual search."""
|
|
356
503
|
project_manager = ProjectManager(project_root)
|
|
357
504
|
config = project_manager.load_config()
|
|
358
|
-
|
|
505
|
+
|
|
359
506
|
embedding_function, _ = create_embedding_function(config.embedding_model)
|
|
360
507
|
database = ChromaVectorDatabase(
|
|
361
508
|
persist_directory=config.index_path,
|
|
362
509
|
embedding_function=embedding_function,
|
|
363
510
|
)
|
|
364
|
-
|
|
511
|
+
|
|
365
512
|
search_engine = SemanticSearchEngine(
|
|
366
513
|
database=database,
|
|
367
514
|
project_root=project_root,
|
|
368
515
|
similarity_threshold=config.similarity_threshold,
|
|
369
516
|
)
|
|
370
|
-
|
|
517
|
+
|
|
371
518
|
async with database:
|
|
372
519
|
results = await search_engine.search_by_context(
|
|
373
520
|
context_description=description,
|
|
374
521
|
focus_areas=focus_areas,
|
|
375
522
|
limit=limit,
|
|
376
523
|
)
|
|
377
|
-
|
|
524
|
+
|
|
378
525
|
if json_output:
|
|
379
526
|
from ..output import print_json
|
|
527
|
+
|
|
380
528
|
results_data = [result.to_dict() for result in results]
|
|
381
529
|
print_json(results_data, title="Context Search Results")
|
|
382
530
|
else:
|
|
383
531
|
query_desc = description
|
|
384
532
|
if focus_areas:
|
|
385
533
|
query_desc += f" (focus: {', '.join(focus_areas)})"
|
|
386
|
-
|
|
534
|
+
|
|
387
535
|
print_search_results(
|
|
388
536
|
results=results,
|
|
389
537
|
query=query_desc,
|
|
@@ -391,5 +539,11 @@ async def run_context_search(
|
|
|
391
539
|
)
|
|
392
540
|
|
|
393
541
|
|
|
542
|
+
# Add commands to search_app for backward compatibility
|
|
543
|
+
search_app.command("main")(search_main)
|
|
544
|
+
search_app.command("similar")(search_similar_cmd)
|
|
545
|
+
search_app.command("context")(search_context_cmd)
|
|
546
|
+
|
|
547
|
+
|
|
394
548
|
if __name__ == "__main__":
|
|
395
549
|
search_app()
|