mcp-vector-search 0.15.7__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 +10 -0
- mcp_vector_search/cli/__init__.py +1 -0
- mcp_vector_search/cli/commands/__init__.py +1 -0
- mcp_vector_search/cli/commands/auto_index.py +397 -0
- mcp_vector_search/cli/commands/chat.py +534 -0
- mcp_vector_search/cli/commands/config.py +393 -0
- mcp_vector_search/cli/commands/demo.py +358 -0
- mcp_vector_search/cli/commands/index.py +762 -0
- mcp_vector_search/cli/commands/init.py +658 -0
- mcp_vector_search/cli/commands/install.py +869 -0
- mcp_vector_search/cli/commands/install_old.py +700 -0
- mcp_vector_search/cli/commands/mcp.py +1254 -0
- mcp_vector_search/cli/commands/reset.py +393 -0
- mcp_vector_search/cli/commands/search.py +796 -0
- mcp_vector_search/cli/commands/setup.py +1133 -0
- mcp_vector_search/cli/commands/status.py +584 -0
- mcp_vector_search/cli/commands/uninstall.py +404 -0
- mcp_vector_search/cli/commands/visualize/__init__.py +39 -0
- mcp_vector_search/cli/commands/visualize/cli.py +265 -0
- mcp_vector_search/cli/commands/visualize/exporters/__init__.py +12 -0
- mcp_vector_search/cli/commands/visualize/exporters/html_exporter.py +33 -0
- mcp_vector_search/cli/commands/visualize/exporters/json_exporter.py +29 -0
- mcp_vector_search/cli/commands/visualize/graph_builder.py +709 -0
- mcp_vector_search/cli/commands/visualize/layout_engine.py +469 -0
- mcp_vector_search/cli/commands/visualize/server.py +201 -0
- mcp_vector_search/cli/commands/visualize/state_manager.py +428 -0
- mcp_vector_search/cli/commands/visualize/templates/__init__.py +16 -0
- mcp_vector_search/cli/commands/visualize/templates/base.py +218 -0
- mcp_vector_search/cli/commands/visualize/templates/scripts.py +3670 -0
- mcp_vector_search/cli/commands/visualize/templates/styles.py +779 -0
- mcp_vector_search/cli/commands/visualize.py.original +2536 -0
- mcp_vector_search/cli/commands/watch.py +287 -0
- mcp_vector_search/cli/didyoumean.py +520 -0
- mcp_vector_search/cli/export.py +320 -0
- mcp_vector_search/cli/history.py +295 -0
- mcp_vector_search/cli/interactive.py +342 -0
- mcp_vector_search/cli/main.py +484 -0
- mcp_vector_search/cli/output.py +414 -0
- mcp_vector_search/cli/suggestions.py +375 -0
- mcp_vector_search/config/__init__.py +1 -0
- mcp_vector_search/config/constants.py +24 -0
- mcp_vector_search/config/defaults.py +200 -0
- mcp_vector_search/config/settings.py +146 -0
- mcp_vector_search/core/__init__.py +1 -0
- mcp_vector_search/core/auto_indexer.py +298 -0
- mcp_vector_search/core/config_utils.py +394 -0
- mcp_vector_search/core/connection_pool.py +360 -0
- mcp_vector_search/core/database.py +1237 -0
- mcp_vector_search/core/directory_index.py +318 -0
- mcp_vector_search/core/embeddings.py +294 -0
- mcp_vector_search/core/exceptions.py +89 -0
- mcp_vector_search/core/factory.py +318 -0
- mcp_vector_search/core/git_hooks.py +345 -0
- mcp_vector_search/core/indexer.py +1002 -0
- mcp_vector_search/core/llm_client.py +453 -0
- mcp_vector_search/core/models.py +294 -0
- mcp_vector_search/core/project.py +350 -0
- mcp_vector_search/core/scheduler.py +330 -0
- mcp_vector_search/core/search.py +952 -0
- mcp_vector_search/core/watcher.py +322 -0
- mcp_vector_search/mcp/__init__.py +5 -0
- mcp_vector_search/mcp/__main__.py +25 -0
- mcp_vector_search/mcp/server.py +752 -0
- mcp_vector_search/parsers/__init__.py +8 -0
- mcp_vector_search/parsers/base.py +296 -0
- mcp_vector_search/parsers/dart.py +605 -0
- mcp_vector_search/parsers/html.py +413 -0
- mcp_vector_search/parsers/javascript.py +643 -0
- mcp_vector_search/parsers/php.py +694 -0
- mcp_vector_search/parsers/python.py +502 -0
- mcp_vector_search/parsers/registry.py +223 -0
- mcp_vector_search/parsers/ruby.py +678 -0
- mcp_vector_search/parsers/text.py +186 -0
- mcp_vector_search/parsers/utils.py +265 -0
- mcp_vector_search/py.typed +1 -0
- mcp_vector_search/utils/__init__.py +42 -0
- mcp_vector_search/utils/gitignore.py +250 -0
- mcp_vector_search/utils/gitignore_updater.py +212 -0
- mcp_vector_search/utils/monorepo.py +339 -0
- mcp_vector_search/utils/timing.py +338 -0
- mcp_vector_search/utils/version.py +47 -0
- mcp_vector_search-0.15.7.dist-info/METADATA +884 -0
- mcp_vector_search-0.15.7.dist-info/RECORD +86 -0
- mcp_vector_search-0.15.7.dist-info/WHEEL +4 -0
- mcp_vector_search-0.15.7.dist-info/entry_points.txt +3 -0
- mcp_vector_search-0.15.7.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"""Base parser interface for MCP Vector Search."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from ..config.constants import DEFAULT_CHUNK_SIZE
|
|
7
|
+
from ..core.models import CodeChunk
|
|
8
|
+
from . import utils
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BaseParser(ABC):
|
|
12
|
+
"""Abstract base class for language parsers."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, language: str) -> None:
|
|
15
|
+
"""Initialize parser for a specific language.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
language: Programming language name
|
|
19
|
+
"""
|
|
20
|
+
self.language = language
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
async def parse_file(self, file_path: Path) -> list[CodeChunk]:
|
|
24
|
+
"""Parse a file and extract code chunks.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
file_path: Path to the file to parse
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
List of code chunks extracted from the file
|
|
31
|
+
"""
|
|
32
|
+
...
|
|
33
|
+
|
|
34
|
+
@abstractmethod
|
|
35
|
+
async def parse_content(self, content: str, file_path: Path) -> list[CodeChunk]:
|
|
36
|
+
"""Parse content and extract code chunks.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
content: File content to parse
|
|
40
|
+
file_path: Path to the source file (for metadata)
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
List of code chunks extracted from the content
|
|
44
|
+
"""
|
|
45
|
+
...
|
|
46
|
+
|
|
47
|
+
def supports_file(self, file_path: Path) -> bool:
|
|
48
|
+
"""Check if this parser supports the given file.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
file_path: Path to check
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
True if this parser can handle the file
|
|
55
|
+
"""
|
|
56
|
+
return file_path.suffix.lower() in self.get_supported_extensions()
|
|
57
|
+
|
|
58
|
+
@abstractmethod
|
|
59
|
+
def get_supported_extensions(self) -> list[str]:
|
|
60
|
+
"""Get list of file extensions supported by this parser.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
List of file extensions (including the dot)
|
|
64
|
+
"""
|
|
65
|
+
...
|
|
66
|
+
|
|
67
|
+
def _calculate_complexity(self, node, language: str | None = None) -> float:
|
|
68
|
+
"""Calculate cyclomatic complexity from AST node.
|
|
69
|
+
|
|
70
|
+
Cyclomatic complexity = Number of decision points + 1
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
node: AST node (tree-sitter)
|
|
74
|
+
language: Programming language for language-specific patterns (defaults to self.language)
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Complexity score (1.0 = simple, 10+ = complex)
|
|
78
|
+
"""
|
|
79
|
+
if language is None:
|
|
80
|
+
language = self.language
|
|
81
|
+
|
|
82
|
+
if not hasattr(node, "children"):
|
|
83
|
+
return 1.0
|
|
84
|
+
|
|
85
|
+
complexity = 1.0 # Base complexity
|
|
86
|
+
|
|
87
|
+
# Language-specific decision node types
|
|
88
|
+
decision_nodes = {
|
|
89
|
+
"python": {
|
|
90
|
+
"if_statement",
|
|
91
|
+
"elif_clause",
|
|
92
|
+
"while_statement",
|
|
93
|
+
"for_statement",
|
|
94
|
+
"except_clause",
|
|
95
|
+
"with_statement",
|
|
96
|
+
"conditional_expression",
|
|
97
|
+
"boolean_operator", # and, or
|
|
98
|
+
},
|
|
99
|
+
"javascript": {
|
|
100
|
+
"if_statement",
|
|
101
|
+
"while_statement",
|
|
102
|
+
"for_statement",
|
|
103
|
+
"for_in_statement",
|
|
104
|
+
"switch_case",
|
|
105
|
+
"catch_clause",
|
|
106
|
+
"conditional_expression",
|
|
107
|
+
"ternary_expression",
|
|
108
|
+
},
|
|
109
|
+
"typescript": {
|
|
110
|
+
"if_statement",
|
|
111
|
+
"while_statement",
|
|
112
|
+
"for_statement",
|
|
113
|
+
"for_in_statement",
|
|
114
|
+
"switch_case",
|
|
115
|
+
"catch_clause",
|
|
116
|
+
"conditional_expression",
|
|
117
|
+
"ternary_expression",
|
|
118
|
+
},
|
|
119
|
+
"dart": {
|
|
120
|
+
"if_statement",
|
|
121
|
+
"while_statement",
|
|
122
|
+
"for_statement",
|
|
123
|
+
"for_in_statement",
|
|
124
|
+
"switch_case",
|
|
125
|
+
"catch_clause",
|
|
126
|
+
"conditional_expression",
|
|
127
|
+
},
|
|
128
|
+
"php": {
|
|
129
|
+
"if_statement",
|
|
130
|
+
"elseif_clause",
|
|
131
|
+
"while_statement",
|
|
132
|
+
"foreach_statement",
|
|
133
|
+
"for_statement",
|
|
134
|
+
"switch_case",
|
|
135
|
+
"catch_clause",
|
|
136
|
+
"ternary_expression",
|
|
137
|
+
},
|
|
138
|
+
"ruby": {
|
|
139
|
+
"if",
|
|
140
|
+
"unless",
|
|
141
|
+
"while",
|
|
142
|
+
"until",
|
|
143
|
+
"for",
|
|
144
|
+
"case",
|
|
145
|
+
"rescue",
|
|
146
|
+
"conditional",
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
nodes_to_count = decision_nodes.get(
|
|
151
|
+
language, decision_nodes.get("python", set())
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
def count_decision_points(n):
|
|
155
|
+
nonlocal complexity
|
|
156
|
+
if hasattr(n, "type") and n.type in nodes_to_count:
|
|
157
|
+
complexity += 1
|
|
158
|
+
if hasattr(n, "children"):
|
|
159
|
+
for child in n.children:
|
|
160
|
+
count_decision_points(child)
|
|
161
|
+
|
|
162
|
+
count_decision_points(node)
|
|
163
|
+
return complexity
|
|
164
|
+
|
|
165
|
+
def _create_chunk(
|
|
166
|
+
self,
|
|
167
|
+
content: str,
|
|
168
|
+
file_path: Path,
|
|
169
|
+
start_line: int,
|
|
170
|
+
end_line: int,
|
|
171
|
+
chunk_type: str = "code",
|
|
172
|
+
function_name: str | None = None,
|
|
173
|
+
class_name: str | None = None,
|
|
174
|
+
docstring: str | None = None,
|
|
175
|
+
complexity_score: float = 0.0,
|
|
176
|
+
decorators: list[str] | None = None,
|
|
177
|
+
parameters: list[dict] | None = None,
|
|
178
|
+
return_type: str | None = None,
|
|
179
|
+
chunk_id: str | None = None,
|
|
180
|
+
parent_chunk_id: str | None = None,
|
|
181
|
+
chunk_depth: int = 0,
|
|
182
|
+
) -> CodeChunk:
|
|
183
|
+
"""Create a code chunk with metadata.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
content: Code content
|
|
187
|
+
file_path: Source file path
|
|
188
|
+
start_line: Starting line number (1-based)
|
|
189
|
+
end_line: Ending line number (1-based)
|
|
190
|
+
chunk_type: Type of chunk (code, function, class, etc.)
|
|
191
|
+
function_name: Function name if applicable
|
|
192
|
+
class_name: Class name if applicable
|
|
193
|
+
docstring: Docstring if applicable
|
|
194
|
+
complexity_score: Cyclomatic complexity score
|
|
195
|
+
decorators: List of decorators/annotations
|
|
196
|
+
parameters: List of function parameters with metadata
|
|
197
|
+
return_type: Return type annotation
|
|
198
|
+
chunk_id: Unique chunk identifier
|
|
199
|
+
parent_chunk_id: Parent chunk ID for hierarchical relationships
|
|
200
|
+
chunk_depth: Nesting level in code hierarchy
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
CodeChunk instance
|
|
204
|
+
"""
|
|
205
|
+
return CodeChunk(
|
|
206
|
+
content=content.strip(),
|
|
207
|
+
file_path=file_path,
|
|
208
|
+
start_line=start_line,
|
|
209
|
+
end_line=end_line,
|
|
210
|
+
language=self.language,
|
|
211
|
+
chunk_type=chunk_type,
|
|
212
|
+
function_name=function_name,
|
|
213
|
+
class_name=class_name,
|
|
214
|
+
docstring=docstring,
|
|
215
|
+
complexity_score=complexity_score,
|
|
216
|
+
decorators=decorators or [],
|
|
217
|
+
parameters=parameters or [],
|
|
218
|
+
return_type=return_type,
|
|
219
|
+
chunk_id=chunk_id,
|
|
220
|
+
parent_chunk_id=parent_chunk_id,
|
|
221
|
+
chunk_depth=chunk_depth,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
def _split_into_lines(self, content: str) -> list[str]:
|
|
225
|
+
"""Split content into lines, preserving line endings.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
content: Content to split
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
List of lines
|
|
232
|
+
"""
|
|
233
|
+
return utils.split_into_lines(content)
|
|
234
|
+
|
|
235
|
+
def _get_line_range(self, lines: list[str], start_line: int, end_line: int) -> str:
|
|
236
|
+
"""Extract a range of lines from content.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
lines: List of lines
|
|
240
|
+
start_line: Starting line number (1-based)
|
|
241
|
+
end_line: Ending line number (1-based)
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
Content for the specified line range
|
|
245
|
+
"""
|
|
246
|
+
return utils.get_line_range(lines, start_line, end_line)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class FallbackParser(BaseParser):
|
|
250
|
+
"""Fallback parser for unsupported languages using simple text chunking."""
|
|
251
|
+
|
|
252
|
+
def __init__(self, language: str = "text") -> None:
|
|
253
|
+
"""Initialize fallback parser."""
|
|
254
|
+
super().__init__(language)
|
|
255
|
+
|
|
256
|
+
async def parse_file(self, file_path: Path) -> list[CodeChunk]:
|
|
257
|
+
"""Parse file using simple text chunking."""
|
|
258
|
+
try:
|
|
259
|
+
with open(file_path, encoding="utf-8") as f:
|
|
260
|
+
content = f.read()
|
|
261
|
+
return await self.parse_content(content, file_path)
|
|
262
|
+
except Exception:
|
|
263
|
+
# Return empty list if file can't be read
|
|
264
|
+
return []
|
|
265
|
+
|
|
266
|
+
async def parse_content(self, content: str, file_path: Path) -> list[CodeChunk]:
|
|
267
|
+
"""Parse content using simple text chunking."""
|
|
268
|
+
if not content.strip():
|
|
269
|
+
return []
|
|
270
|
+
|
|
271
|
+
lines = self._split_into_lines(content)
|
|
272
|
+
chunks = []
|
|
273
|
+
|
|
274
|
+
# Simple chunking: split into chunks of ~50 lines
|
|
275
|
+
chunk_size = DEFAULT_CHUNK_SIZE
|
|
276
|
+
for i in range(0, len(lines), chunk_size):
|
|
277
|
+
start_line = i + 1
|
|
278
|
+
end_line = min(i + chunk_size, len(lines))
|
|
279
|
+
|
|
280
|
+
chunk_content = self._get_line_range(lines, start_line, end_line)
|
|
281
|
+
|
|
282
|
+
if chunk_content.strip():
|
|
283
|
+
chunk = self._create_chunk(
|
|
284
|
+
content=chunk_content,
|
|
285
|
+
file_path=file_path,
|
|
286
|
+
start_line=start_line,
|
|
287
|
+
end_line=end_line,
|
|
288
|
+
chunk_type="text",
|
|
289
|
+
)
|
|
290
|
+
chunks.append(chunk)
|
|
291
|
+
|
|
292
|
+
return chunks
|
|
293
|
+
|
|
294
|
+
def get_supported_extensions(self) -> list[str]:
|
|
295
|
+
"""Fallback parser supports all extensions."""
|
|
296
|
+
return ["*"] # Special marker for "all extensions"
|