mcp-vector-search 0.4.14__py3-none-any.whl → 0.5.0__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 +73 -31
- mcp_vector_search/cli/commands/init.py +189 -113
- mcp_vector_search/cli/commands/install.py +525 -113
- mcp_vector_search/cli/commands/mcp.py +201 -151
- mcp_vector_search/cli/commands/reset.py +41 -41
- mcp_vector_search/cli/commands/search.py +73 -14
- mcp_vector_search/cli/commands/status.py +51 -17
- mcp_vector_search/cli/didyoumean.py +254 -246
- mcp_vector_search/cli/main.py +114 -43
- mcp_vector_search/cli/output.py +152 -0
- mcp_vector_search/cli/suggestions.py +246 -197
- mcp_vector_search/core/database.py +81 -49
- mcp_vector_search/core/indexer.py +10 -4
- mcp_vector_search/core/search.py +17 -6
- mcp_vector_search/mcp/__main__.py +1 -1
- mcp_vector_search/mcp/server.py +211 -203
- mcp_vector_search/parsers/__init__.py +6 -0
- mcp_vector_search/parsers/dart.py +605 -0
- mcp_vector_search/parsers/php.py +694 -0
- mcp_vector_search/parsers/registry.py +16 -1
- mcp_vector_search/parsers/ruby.py +678 -0
- mcp_vector_search/parsers/text.py +31 -25
- mcp_vector_search/utils/gitignore.py +72 -71
- {mcp_vector_search-0.4.14.dist-info → mcp_vector_search-0.5.0.dist-info}/METADATA +59 -2
- {mcp_vector_search-0.4.14.dist-info → mcp_vector_search-0.5.0.dist-info}/RECORD +29 -26
- {mcp_vector_search-0.4.14.dist-info → mcp_vector_search-0.5.0.dist-info}/WHEEL +0 -0
- {mcp_vector_search-0.4.14.dist-info → mcp_vector_search-0.5.0.dist-info}/entry_points.txt +0 -0
- {mcp_vector_search-0.4.14.dist-info → mcp_vector_search-0.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -48,10 +48,10 @@ class TextParser(BaseParser):
|
|
|
48
48
|
|
|
49
49
|
chunks = []
|
|
50
50
|
lines = content.splitlines(keepends=True)
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
# Try paragraph-based chunking first
|
|
53
53
|
paragraphs = self._extract_paragraphs(content)
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
if paragraphs:
|
|
56
56
|
# Use paragraph-based chunking
|
|
57
57
|
for para_info in paragraphs:
|
|
@@ -70,9 +70,9 @@ class TextParser(BaseParser):
|
|
|
70
70
|
for i in range(0, len(lines), chunk_size):
|
|
71
71
|
start_line = i + 1
|
|
72
72
|
end_line = min(i + chunk_size, len(lines))
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
chunk_content = "".join(lines[i:end_line])
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
if chunk_content.strip():
|
|
77
77
|
chunk = self._create_chunk(
|
|
78
78
|
content=chunk_content,
|
|
@@ -82,7 +82,7 @@ class TextParser(BaseParser):
|
|
|
82
82
|
chunk_type="text",
|
|
83
83
|
)
|
|
84
84
|
chunks.append(chunk)
|
|
85
|
-
|
|
85
|
+
|
|
86
86
|
return chunks
|
|
87
87
|
|
|
88
88
|
def _extract_paragraphs(self, content: str) -> list[dict]:
|
|
@@ -101,7 +101,7 @@ class TextParser(BaseParser):
|
|
|
101
101
|
paragraphs = []
|
|
102
102
|
current_para = []
|
|
103
103
|
start_line = 1
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
for i, line in enumerate(lines, 1):
|
|
106
106
|
if line.strip():
|
|
107
107
|
if not current_para:
|
|
@@ -112,31 +112,37 @@ class TextParser(BaseParser):
|
|
|
112
112
|
# End of paragraph
|
|
113
113
|
para_content = "".join(current_para)
|
|
114
114
|
if len(para_content.strip()) > 20: # Minimum paragraph size
|
|
115
|
-
paragraphs.append(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
paragraphs.append(
|
|
116
|
+
{
|
|
117
|
+
"content": para_content,
|
|
118
|
+
"start_line": start_line,
|
|
119
|
+
"end_line": i - 1,
|
|
120
|
+
}
|
|
121
|
+
)
|
|
120
122
|
current_para = []
|
|
121
|
-
|
|
123
|
+
|
|
122
124
|
# Handle last paragraph if exists
|
|
123
125
|
if current_para:
|
|
124
126
|
para_content = "".join(current_para)
|
|
125
127
|
if len(para_content.strip()) > 20:
|
|
126
|
-
paragraphs.append(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
128
|
+
paragraphs.append(
|
|
129
|
+
{
|
|
130
|
+
"content": para_content,
|
|
131
|
+
"start_line": start_line,
|
|
132
|
+
"end_line": len(lines),
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
|
|
132
136
|
# If we have very few paragraphs, merge small ones
|
|
133
137
|
if paragraphs:
|
|
134
138
|
merged = self._merge_small_paragraphs(paragraphs)
|
|
135
139
|
return merged
|
|
136
|
-
|
|
140
|
+
|
|
137
141
|
return []
|
|
138
142
|
|
|
139
|
-
def _merge_small_paragraphs(
|
|
143
|
+
def _merge_small_paragraphs(
|
|
144
|
+
self, paragraphs: list[dict], target_size: int = 200
|
|
145
|
+
) -> list[dict]:
|
|
140
146
|
"""Merge small paragraphs to create more substantial chunks.
|
|
141
147
|
|
|
142
148
|
Args:
|
|
@@ -148,10 +154,10 @@ class TextParser(BaseParser):
|
|
|
148
154
|
"""
|
|
149
155
|
merged = []
|
|
150
156
|
current_merge = None
|
|
151
|
-
|
|
157
|
+
|
|
152
158
|
for para in paragraphs:
|
|
153
159
|
para_len = len(para["content"])
|
|
154
|
-
|
|
160
|
+
|
|
155
161
|
if current_merge is None:
|
|
156
162
|
current_merge = para.copy()
|
|
157
163
|
elif len(current_merge["content"]) + para_len < target_size * 2:
|
|
@@ -163,11 +169,11 @@ class TextParser(BaseParser):
|
|
|
163
169
|
if len(current_merge["content"].strip()) > 20:
|
|
164
170
|
merged.append(current_merge)
|
|
165
171
|
current_merge = para.copy()
|
|
166
|
-
|
|
172
|
+
|
|
167
173
|
# Add last merge
|
|
168
174
|
if current_merge and len(current_merge["content"].strip()) > 20:
|
|
169
175
|
merged.append(current_merge)
|
|
170
|
-
|
|
176
|
+
|
|
171
177
|
return merged
|
|
172
178
|
|
|
173
179
|
def get_supported_extensions(self) -> list[str]:
|
|
@@ -176,4 +182,4 @@ class TextParser(BaseParser):
|
|
|
176
182
|
Returns:
|
|
177
183
|
List of supported extensions
|
|
178
184
|
"""
|
|
179
|
-
return [".txt"]
|
|
185
|
+
return [".txt"]
|
|
@@ -3,17 +3,18 @@
|
|
|
3
3
|
import fnmatch
|
|
4
4
|
import re
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import List, Set
|
|
7
6
|
|
|
8
7
|
from loguru import logger
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
class GitignorePattern:
|
|
12
11
|
"""Represents a single gitignore pattern with its matching logic."""
|
|
13
|
-
|
|
14
|
-
def __init__(
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self, pattern: str, is_negation: bool = False, is_directory_only: bool = False
|
|
15
|
+
):
|
|
15
16
|
"""Initialize a gitignore pattern.
|
|
16
|
-
|
|
17
|
+
|
|
17
18
|
Args:
|
|
18
19
|
pattern: The pattern string
|
|
19
20
|
is_negation: Whether this is a negation pattern (starts with !)
|
|
@@ -23,140 +24,140 @@ class GitignorePattern:
|
|
|
23
24
|
self.is_negation = is_negation
|
|
24
25
|
self.is_directory_only = is_directory_only
|
|
25
26
|
self.pattern = self._normalize_pattern(pattern)
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
def _normalize_pattern(self, pattern: str) -> str:
|
|
28
29
|
"""Normalize the pattern for matching."""
|
|
29
30
|
# Remove leading ! for negation patterns
|
|
30
|
-
if pattern.startswith(
|
|
31
|
+
if pattern.startswith("!"):
|
|
31
32
|
pattern = pattern[1:]
|
|
32
|
-
|
|
33
|
+
|
|
33
34
|
# Remove trailing / for directory-only patterns
|
|
34
|
-
if pattern.endswith(
|
|
35
|
+
if pattern.endswith("/"):
|
|
35
36
|
pattern = pattern[:-1]
|
|
36
|
-
|
|
37
|
+
|
|
37
38
|
# Handle leading slash (absolute from repo root)
|
|
38
|
-
if pattern.startswith(
|
|
39
|
+
if pattern.startswith("/"):
|
|
39
40
|
pattern = pattern[1:]
|
|
40
|
-
|
|
41
|
+
|
|
41
42
|
return pattern
|
|
42
|
-
|
|
43
|
+
|
|
43
44
|
def matches(self, path: str, is_directory: bool = False) -> bool:
|
|
44
45
|
"""Check if this pattern matches the given path.
|
|
45
|
-
|
|
46
|
+
|
|
46
47
|
Args:
|
|
47
48
|
path: Relative path from repository root
|
|
48
49
|
is_directory: Whether the path is a directory
|
|
49
|
-
|
|
50
|
+
|
|
50
51
|
Returns:
|
|
51
52
|
True if the pattern matches
|
|
52
53
|
"""
|
|
53
54
|
# Directory-only patterns only match directories
|
|
54
55
|
if self.is_directory_only and not is_directory:
|
|
55
56
|
return False
|
|
56
|
-
|
|
57
|
+
|
|
57
58
|
# Convert path separators for consistent matching
|
|
58
|
-
path = path.replace(
|
|
59
|
-
pattern = self.pattern.replace(
|
|
60
|
-
|
|
59
|
+
path = path.replace("\\", "/")
|
|
60
|
+
pattern = self.pattern.replace("\\", "/")
|
|
61
|
+
|
|
61
62
|
# Try exact match first
|
|
62
63
|
if fnmatch.fnmatch(path, pattern):
|
|
63
64
|
return True
|
|
64
|
-
|
|
65
|
+
|
|
65
66
|
# Try matching any parent directory
|
|
66
|
-
path_parts = path.split(
|
|
67
|
+
path_parts = path.split("/")
|
|
67
68
|
for i in range(len(path_parts)):
|
|
68
|
-
subpath =
|
|
69
|
+
subpath = "/".join(path_parts[i:])
|
|
69
70
|
if fnmatch.fnmatch(subpath, pattern):
|
|
70
71
|
return True
|
|
71
|
-
|
|
72
|
+
|
|
72
73
|
# Try matching with ** patterns (glob-style)
|
|
73
|
-
if
|
|
74
|
+
if "**" in pattern:
|
|
74
75
|
# Convert ** to regex pattern
|
|
75
|
-
regex_pattern = pattern.replace(
|
|
76
|
-
regex_pattern = regex_pattern.replace(
|
|
77
|
-
regex_pattern = regex_pattern.replace(
|
|
78
|
-
regex_pattern = f
|
|
79
|
-
|
|
76
|
+
regex_pattern = pattern.replace("**", ".*")
|
|
77
|
+
regex_pattern = regex_pattern.replace("*", "[^/]*")
|
|
78
|
+
regex_pattern = regex_pattern.replace("?", "[^/]")
|
|
79
|
+
regex_pattern = f"^{regex_pattern}$"
|
|
80
|
+
|
|
80
81
|
try:
|
|
81
82
|
if re.match(regex_pattern, path):
|
|
82
83
|
return True
|
|
83
84
|
except re.error:
|
|
84
85
|
# Fallback to simple fnmatch if regex fails
|
|
85
86
|
pass
|
|
86
|
-
|
|
87
|
+
|
|
87
88
|
return False
|
|
88
89
|
|
|
89
90
|
|
|
90
91
|
class GitignoreParser:
|
|
91
92
|
"""Parser for .gitignore files with proper pattern matching."""
|
|
92
|
-
|
|
93
|
+
|
|
93
94
|
def __init__(self, project_root: Path):
|
|
94
95
|
"""Initialize gitignore parser.
|
|
95
|
-
|
|
96
|
+
|
|
96
97
|
Args:
|
|
97
98
|
project_root: Root directory of the project
|
|
98
99
|
"""
|
|
99
100
|
self.project_root = project_root
|
|
100
|
-
self.patterns:
|
|
101
|
+
self.patterns: list[GitignorePattern] = []
|
|
101
102
|
self._load_gitignore_files()
|
|
102
|
-
|
|
103
|
+
|
|
103
104
|
def _load_gitignore_files(self) -> None:
|
|
104
105
|
"""Load all .gitignore files in the project hierarchy."""
|
|
105
106
|
# Load global .gitignore first (if exists)
|
|
106
|
-
global_gitignore = self.project_root /
|
|
107
|
+
global_gitignore = self.project_root / ".gitignore"
|
|
107
108
|
if global_gitignore.exists():
|
|
108
109
|
self._parse_gitignore_file(global_gitignore)
|
|
109
|
-
|
|
110
|
+
|
|
110
111
|
# Load .gitignore files in subdirectories
|
|
111
|
-
for gitignore_file in self.project_root.rglob(
|
|
112
|
+
for gitignore_file in self.project_root.rglob(".gitignore"):
|
|
112
113
|
if gitignore_file != global_gitignore:
|
|
113
114
|
self._parse_gitignore_file(gitignore_file)
|
|
114
|
-
|
|
115
|
+
|
|
115
116
|
def _parse_gitignore_file(self, gitignore_path: Path) -> None:
|
|
116
117
|
"""Parse a single .gitignore file.
|
|
117
|
-
|
|
118
|
+
|
|
118
119
|
Args:
|
|
119
120
|
gitignore_path: Path to the .gitignore file
|
|
120
121
|
"""
|
|
121
122
|
try:
|
|
122
|
-
with open(gitignore_path,
|
|
123
|
+
with open(gitignore_path, encoding="utf-8", errors="ignore") as f:
|
|
123
124
|
lines = f.readlines()
|
|
124
|
-
|
|
125
|
+
|
|
125
126
|
for line_num, line in enumerate(lines, 1):
|
|
126
127
|
line = line.strip()
|
|
127
|
-
|
|
128
|
+
|
|
128
129
|
# Skip empty lines and comments
|
|
129
|
-
if not line or line.startswith(
|
|
130
|
+
if not line or line.startswith("#"):
|
|
130
131
|
continue
|
|
131
|
-
|
|
132
|
+
|
|
132
133
|
# Check for negation pattern
|
|
133
|
-
is_negation = line.startswith(
|
|
134
|
-
|
|
134
|
+
is_negation = line.startswith("!")
|
|
135
|
+
|
|
135
136
|
# Check for directory-only pattern
|
|
136
|
-
is_directory_only = line.endswith(
|
|
137
|
-
|
|
137
|
+
is_directory_only = line.endswith("/")
|
|
138
|
+
|
|
138
139
|
# Create pattern relative to the .gitignore file's directory
|
|
139
140
|
gitignore_dir = gitignore_path.parent
|
|
140
141
|
if gitignore_dir != self.project_root:
|
|
141
142
|
# Adjust pattern for subdirectory .gitignore files
|
|
142
143
|
relative_dir = gitignore_dir.relative_to(self.project_root)
|
|
143
|
-
if not line.startswith(
|
|
144
|
+
if not line.startswith("/") and not is_negation:
|
|
144
145
|
line = str(relative_dir / line)
|
|
145
|
-
elif is_negation and not line[1:].startswith(
|
|
146
|
-
line =
|
|
147
|
-
|
|
146
|
+
elif is_negation and not line[1:].startswith("/"):
|
|
147
|
+
line = "!" + str(relative_dir / line[1:])
|
|
148
|
+
|
|
148
149
|
pattern = GitignorePattern(line, is_negation, is_directory_only)
|
|
149
150
|
self.patterns.append(pattern)
|
|
150
|
-
|
|
151
|
+
|
|
151
152
|
except Exception as e:
|
|
152
153
|
logger.warning(f"Failed to parse {gitignore_path}: {e}")
|
|
153
|
-
|
|
154
|
+
|
|
154
155
|
def is_ignored(self, path: Path) -> bool:
|
|
155
156
|
"""Check if a path should be ignored according to .gitignore rules.
|
|
156
|
-
|
|
157
|
+
|
|
157
158
|
Args:
|
|
158
159
|
path: Path to check (can be absolute or relative to project root)
|
|
159
|
-
|
|
160
|
+
|
|
160
161
|
Returns:
|
|
161
162
|
True if the path should be ignored
|
|
162
163
|
"""
|
|
@@ -166,37 +167,37 @@ class GitignoreParser:
|
|
|
166
167
|
relative_path = path.relative_to(self.project_root)
|
|
167
168
|
else:
|
|
168
169
|
relative_path = path
|
|
169
|
-
|
|
170
|
-
path_str = str(relative_path).replace(
|
|
170
|
+
|
|
171
|
+
path_str = str(relative_path).replace("\\", "/")
|
|
171
172
|
is_directory = path.is_dir() if path.exists() else False
|
|
172
|
-
|
|
173
|
+
|
|
173
174
|
# Apply patterns in order, with later patterns overriding earlier ones
|
|
174
175
|
ignored = False
|
|
175
|
-
|
|
176
|
+
|
|
176
177
|
for pattern in self.patterns:
|
|
177
178
|
if pattern.matches(path_str, is_directory):
|
|
178
179
|
ignored = not pattern.is_negation
|
|
179
|
-
|
|
180
|
+
|
|
180
181
|
return ignored
|
|
181
|
-
|
|
182
|
+
|
|
182
183
|
except ValueError:
|
|
183
184
|
# Path is not relative to project root
|
|
184
185
|
return False
|
|
185
186
|
except Exception as e:
|
|
186
187
|
logger.debug(f"Error checking gitignore for {path}: {e}")
|
|
187
188
|
return False
|
|
188
|
-
|
|
189
|
-
def get_ignored_patterns(self) ->
|
|
189
|
+
|
|
190
|
+
def get_ignored_patterns(self) -> list[str]:
|
|
190
191
|
"""Get list of all ignore patterns.
|
|
191
|
-
|
|
192
|
+
|
|
192
193
|
Returns:
|
|
193
194
|
List of pattern strings
|
|
194
195
|
"""
|
|
195
196
|
return [p.original_pattern for p in self.patterns if not p.is_negation]
|
|
196
|
-
|
|
197
|
-
def get_negation_patterns(self) ->
|
|
197
|
+
|
|
198
|
+
def get_negation_patterns(self) -> list[str]:
|
|
198
199
|
"""Get list of all negation patterns.
|
|
199
|
-
|
|
200
|
+
|
|
200
201
|
Returns:
|
|
201
202
|
List of negation pattern strings
|
|
202
203
|
"""
|
|
@@ -205,10 +206,10 @@ class GitignoreParser:
|
|
|
205
206
|
|
|
206
207
|
def create_gitignore_parser(project_root: Path) -> GitignoreParser:
|
|
207
208
|
"""Create a gitignore parser for the given project.
|
|
208
|
-
|
|
209
|
+
|
|
209
210
|
Args:
|
|
210
211
|
project_root: Root directory of the project
|
|
211
|
-
|
|
212
|
+
|
|
212
213
|
Returns:
|
|
213
214
|
GitignoreParser instance
|
|
214
215
|
"""
|
|
@@ -217,11 +218,11 @@ def create_gitignore_parser(project_root: Path) -> GitignoreParser:
|
|
|
217
218
|
|
|
218
219
|
def is_path_gitignored(path: Path, project_root: Path) -> bool:
|
|
219
220
|
"""Quick function to check if a path is gitignored.
|
|
220
|
-
|
|
221
|
+
|
|
221
222
|
Args:
|
|
222
223
|
path: Path to check
|
|
223
224
|
project_root: Root directory of the project
|
|
224
|
-
|
|
225
|
+
|
|
225
226
|
Returns:
|
|
226
227
|
True if the path should be ignored
|
|
227
228
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-vector-search
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: CLI-first semantic code search with MCP integration
|
|
5
5
|
Project-URL: Homepage, https://github.com/bobmatnyc/mcp-vector-search
|
|
6
6
|
Project-URL: Documentation, https://mcp-vector-search.readthedocs.io
|
|
@@ -71,7 +71,7 @@ A modern, fast, and intelligent code search tool that understands your codebase
|
|
|
71
71
|
### 🚀 **Core Capabilities**
|
|
72
72
|
- **Semantic Search**: Find code by meaning, not just keywords
|
|
73
73
|
- **AST-Aware Parsing**: Understands code structure (functions, classes, methods)
|
|
74
|
-
- **Multi-Language Support**: Python, JavaScript, TypeScript (with extensible architecture)
|
|
74
|
+
- **Multi-Language Support**: Python, JavaScript, TypeScript, Dart/Flutter, PHP, Ruby (with extensible architecture)
|
|
75
75
|
- **Real-time Indexing**: File watching with automatic index updates
|
|
76
76
|
- **Local-First**: Complete privacy with on-device processing
|
|
77
77
|
- **Zero Configuration**: Auto-detects project structure and languages
|
|
@@ -108,6 +108,33 @@ cd mcp-vector-search
|
|
|
108
108
|
uv sync && uv pip install -e .
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
+
### Complete Setup with Install Command
|
|
112
|
+
|
|
113
|
+
The new **enhanced install command** provides a complete one-step setup:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Interactive setup with MCP configuration
|
|
117
|
+
mcp-vector-search install
|
|
118
|
+
|
|
119
|
+
# Setup without MCP configuration
|
|
120
|
+
mcp-vector-search install --no-mcp
|
|
121
|
+
|
|
122
|
+
# Setup for specific MCP tool
|
|
123
|
+
mcp-vector-search install --mcp-tool "Claude Code"
|
|
124
|
+
|
|
125
|
+
# Setup without automatic indexing
|
|
126
|
+
mcp-vector-search install --no-index
|
|
127
|
+
|
|
128
|
+
# Custom file extensions
|
|
129
|
+
mcp-vector-search install --extensions .py,.js,.ts,.dart
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The install command:
|
|
133
|
+
- Initializes your project configuration
|
|
134
|
+
- Detects and configures MCP tools (Claude Code, Cursor, Windsurf, VS Code)
|
|
135
|
+
- Automatically indexes your codebase
|
|
136
|
+
- Provides rich progress indicators and next-step hints
|
|
137
|
+
|
|
111
138
|
### Basic Usage
|
|
112
139
|
|
|
113
140
|
```bash
|
|
@@ -333,10 +360,40 @@ Projects are configured via `.mcp-vector-search/config.json`:
|
|
|
333
360
|
| Python | ✅ Full | Functions, classes, methods, docstrings |
|
|
334
361
|
| JavaScript | ✅ Full | Functions, classes, JSDoc, ES6+ syntax |
|
|
335
362
|
| TypeScript | ✅ Full | Interfaces, types, generics, decorators |
|
|
363
|
+
| Dart | ✅ Full | Functions, classes, widgets, async, dartdoc |
|
|
364
|
+
| PHP | ✅ Full | Classes, methods, traits, PHPDoc, Laravel patterns |
|
|
365
|
+
| Ruby | ✅ Full | Modules, classes, methods, RDoc, Rails patterns |
|
|
366
|
+
| Text/Markdown | ✅ Basic | Semantic chunking for documentation |
|
|
336
367
|
| Java | 🔄 Planned | Classes, methods, annotations |
|
|
337
368
|
| Go | 🔄 Planned | Functions, structs, interfaces |
|
|
338
369
|
| Rust | 🔄 Planned | Functions, structs, traits |
|
|
339
370
|
|
|
371
|
+
#### New Language Support
|
|
372
|
+
|
|
373
|
+
**Dart/Flutter Support** (v0.4.15):
|
|
374
|
+
- **Widget Detection**: StatelessWidget, StatefulWidget recognition
|
|
375
|
+
- **State Classes**: Automatic parsing of `_WidgetNameState` patterns
|
|
376
|
+
- **Async Support**: Future<T> and async function handling
|
|
377
|
+
- **Dartdoc**: Triple-slash comment extraction
|
|
378
|
+
- **Tree-sitter AST**: Fast, accurate parsing with regex fallback
|
|
379
|
+
|
|
380
|
+
**PHP Support** (v0.5.0):
|
|
381
|
+
- **Class Detection**: Classes, interfaces, traits
|
|
382
|
+
- **Method Extraction**: Public, private, protected, static methods
|
|
383
|
+
- **Magic Methods**: __construct, __get, __set, __call, etc.
|
|
384
|
+
- **PHPDoc**: Full comment extraction
|
|
385
|
+
- **Laravel Patterns**: Controllers, Models, Eloquent support
|
|
386
|
+
- **Tree-sitter AST**: Fast parsing with regex fallback
|
|
387
|
+
|
|
388
|
+
**Ruby Support** (v0.5.0):
|
|
389
|
+
- **Module/Class Detection**: Full namespace support (::)
|
|
390
|
+
- **Method Extraction**: Instance and class methods
|
|
391
|
+
- **Special Syntax**: Method names with ?, ! support
|
|
392
|
+
- **Attribute Macros**: attr_accessor, attr_reader, attr_writer
|
|
393
|
+
- **RDoc**: Comment extraction (# and =begin...=end)
|
|
394
|
+
- **Rails Patterns**: ActiveRecord, Controllers support
|
|
395
|
+
- **Tree-sitter AST**: Fast parsing with regex fallback
|
|
396
|
+
|
|
340
397
|
## 🤝 Contributing
|
|
341
398
|
|
|
342
399
|
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
mcp_vector_search/__init__.py,sha256=
|
|
1
|
+
mcp_vector_search/__init__.py,sha256=eis7op2vTtnKT4n7nV7zQEHKVUChwmbCwamTLVHDzIU,299
|
|
2
2
|
mcp_vector_search/py.typed,sha256=lCKeV9Qcn9sGtbRsgg-LJO2ZwWRuknnnlmomq3bJFH0,43
|
|
3
3
|
mcp_vector_search/cli/__init__.py,sha256=TNB7CaOASz8u3yHWLbNmo8-GtHF0qwUjVKWAuNphKgo,40
|
|
4
|
-
mcp_vector_search/cli/didyoumean.py,sha256=
|
|
4
|
+
mcp_vector_search/cli/didyoumean.py,sha256=dzHtzGaCRtKGum1KqyPBNP4_7d2JNsxgcT4kD7rIfp4,15941
|
|
5
5
|
mcp_vector_search/cli/export.py,sha256=iluxuRT2KELdKlQeDAlVkteiel4GGrng153UAw9H0as,10804
|
|
6
6
|
mcp_vector_search/cli/history.py,sha256=osQVNiTIdGSRZQiJEjC_AYMmHxaqv7RSKSeuO325Ip0,9115
|
|
7
7
|
mcp_vector_search/cli/interactive.py,sha256=T7P4dAdvbglznzQYgiePv5YNyOx9FeE57Y3OKYnnbYE,12744
|
|
8
|
-
mcp_vector_search/cli/main.py,sha256=
|
|
9
|
-
mcp_vector_search/cli/output.py,sha256=
|
|
10
|
-
mcp_vector_search/cli/suggestions.py,sha256=
|
|
8
|
+
mcp_vector_search/cli/main.py,sha256=b9au8AD_VETgSFeLSpcLYDOdRVVUL6aQms8D_wn84T8,16702
|
|
9
|
+
mcp_vector_search/cli/output.py,sha256=7ShIk_UKzhDzRGxI6JluPu0gGkbmKOevqgIAKR4oCa0,12560
|
|
10
|
+
mcp_vector_search/cli/suggestions.py,sha256=i3KF8AaMa86aT5n64xwdw8R_ZiS-UehnF97fM0YzWAU,13184
|
|
11
11
|
mcp_vector_search/cli/commands/__init__.py,sha256=vQls-YKZ54YEwmf7g1dL0T2SS9D4pdQljXzsUChG_V4,42
|
|
12
12
|
mcp_vector_search/cli/commands/auto_index.py,sha256=imVVbxWRlA128NPdK9BetNNl3ELrsdq-hqcsLqyAmoM,12712
|
|
13
13
|
mcp_vector_search/cli/commands/config.py,sha256=EHLqToCXrZs3gjIAg7pV8Bq8yVslUXWC4AnTcZQgSPQ,11337
|
|
14
|
-
mcp_vector_search/cli/commands/index.py,sha256=
|
|
15
|
-
mcp_vector_search/cli/commands/init.py,sha256=
|
|
16
|
-
mcp_vector_search/cli/commands/install.py,sha256=
|
|
17
|
-
mcp_vector_search/cli/commands/mcp.py,sha256=
|
|
18
|
-
mcp_vector_search/cli/commands/reset.py,sha256=
|
|
19
|
-
mcp_vector_search/cli/commands/search.py,sha256=
|
|
20
|
-
mcp_vector_search/cli/commands/status.py,sha256=
|
|
14
|
+
mcp_vector_search/cli/commands/index.py,sha256=soupoLR3EBsSzjqvzxurT9I6A8ZYM9asWVmJY3bDqMI,15394
|
|
15
|
+
mcp_vector_search/cli/commands/init.py,sha256=orIJo71nETcLZCU6olWNVSz_jNWFhwDYu-1lHEqhkfA,25522
|
|
16
|
+
mcp_vector_search/cli/commands/install.py,sha256=3Eiih--rizmpLNGyeDJlA93n8tSzUUJGm8GwMloVwy0,24617
|
|
17
|
+
mcp_vector_search/cli/commands/mcp.py,sha256=FKZNxYrDc7HfPTFBUEypCv-8atsrHEdbtU6Yfg9QUMA,18569
|
|
18
|
+
mcp_vector_search/cli/commands/reset.py,sha256=3gagCRnW7FRrUzJDgD49WfB7IK2TkQgMFnHRIZSr0zE,13733
|
|
19
|
+
mcp_vector_search/cli/commands/search.py,sha256=PA3jOmWQCdkW-D6vCxodgWf8wpX2S3ZO4j_1OWjlY7E,19143
|
|
20
|
+
mcp_vector_search/cli/commands/status.py,sha256=Aj2KZjnI7DtVUXTNfTcZuAnjK3Zhlt9TC4HWSViQMj8,17595
|
|
21
21
|
mcp_vector_search/cli/commands/watch.py,sha256=2pyWRoo4fIppFnyQ4sW4IBLHmpb_IwnTjRnzHkVBPcQ,8927
|
|
22
22
|
mcp_vector_search/config/__init__.py,sha256=r_qAQkU5gc0EQ2pv8EQARACe4klhrR_WRJqCb9lfGc0,54
|
|
23
23
|
mcp_vector_search/config/defaults.py,sha256=62wM6NGwjLQW8mu0Wp9eIykii2-3dbxuqzszB7spkVw,4945
|
|
@@ -25,32 +25,35 @@ mcp_vector_search/config/settings.py,sha256=v1bc2K2yTwDzQKiy_BQhTWCP7FinSWX99vQG
|
|
|
25
25
|
mcp_vector_search/core/__init__.py,sha256=bWKtKmmaFs7gG5XPCbrx77UYIVeO1FF8wIJxpj1dLNw,48
|
|
26
26
|
mcp_vector_search/core/auto_indexer.py,sha256=0S4lZXaUgqEytMSA2FxQsh5hN7V1mbSLYVzEf_dslYQ,10307
|
|
27
27
|
mcp_vector_search/core/connection_pool.py,sha256=Yo-gUQQbHawtuvh6OcJiAlbbvWQGQBd31QZOvs498fg,11224
|
|
28
|
-
mcp_vector_search/core/database.py,sha256=
|
|
28
|
+
mcp_vector_search/core/database.py,sha256=wh55w42V5FbMX1rgo4zxLNb0t6fzq6UJx8NOBjAqfxs,35533
|
|
29
29
|
mcp_vector_search/core/embeddings.py,sha256=wSMUNxZcuGPMxxQ1AbKqA1a3-0c6AiOqmuuI7OqTyaQ,10578
|
|
30
30
|
mcp_vector_search/core/exceptions.py,sha256=3bCjT8wmrLz_0e_Tayr90049zNTKYFWZa19kl0saKz8,1597
|
|
31
31
|
mcp_vector_search/core/factory.py,sha256=GnsCBcRndBaUBWSXwtt8QKInAo-rGGBTSOKznUFETwA,10016
|
|
32
32
|
mcp_vector_search/core/git_hooks.py,sha256=xOfPpzgKoNTwM-vbhAihUucgudBQk45bCAVR5zJOFlQ,10878
|
|
33
|
-
mcp_vector_search/core/indexer.py,sha256=
|
|
33
|
+
mcp_vector_search/core/indexer.py,sha256=_hY_9llmJdc2j5ct0eugSyMWUGLP1WEh7kzsEnCbroc,15758
|
|
34
34
|
mcp_vector_search/core/models.py,sha256=fnZxvUkd9Afxmdwtw2BJX7uik6rQTwuWBTTqTeqDi0A,6697
|
|
35
35
|
mcp_vector_search/core/project.py,sha256=RNeLBZZw6SO5mXqCwYfhStTGuVgeMq1US5UftG0SBYk,10069
|
|
36
36
|
mcp_vector_search/core/scheduler.py,sha256=PBSlu-ieDYCXOMGYY7QKv9UReFEDPHNmwnUv_xb4vxg,11761
|
|
37
|
-
mcp_vector_search/core/search.py,sha256=
|
|
37
|
+
mcp_vector_search/core/search.py,sha256=ZnrKIEqjLNVnh3_lfsVE0p97EWlNDZxYcc5SHhJUlRE,29787
|
|
38
38
|
mcp_vector_search/core/watcher.py,sha256=-DFRCnuUfcqcTrkZPQqfJSvxKAxnpt-axgEj1V-B0O4,10862
|
|
39
39
|
mcp_vector_search/mcp/__init__.py,sha256=gfKR0QV7Jqvj5y0LMBe9gSghd5_rPsvm_rml0ryQtoY,158
|
|
40
|
-
mcp_vector_search/mcp/__main__.py,sha256=
|
|
41
|
-
mcp_vector_search/mcp/server.py,sha256=
|
|
42
|
-
mcp_vector_search/parsers/__init__.py,sha256=
|
|
40
|
+
mcp_vector_search/mcp/__main__.py,sha256=KgwB59HM5pRLe2Aj-fvDFcTp95lyT0wfmS3ENcx9gPc,571
|
|
41
|
+
mcp_vector_search/mcp/server.py,sha256=YmHyvJqg_CjxEN356ShFrTPLgDKzaLXyrt8tNHVryEY,28322
|
|
42
|
+
mcp_vector_search/parsers/__init__.py,sha256=Qza-RapcVLhBpcuL2CCmf0b5LSzig1ZfhiCOZ5esO8w,185
|
|
43
43
|
mcp_vector_search/parsers/base.py,sha256=-zBY9T0modfegowaNyf5_upkS3ImR4TgrRwoSLuAiDw,5421
|
|
44
|
+
mcp_vector_search/parsers/dart.py,sha256=Cs10Tw3XvvA_nJpUilKgrj8E2Sq6F3urODEFH_ZPS3M,21840
|
|
44
45
|
mcp_vector_search/parsers/javascript.py,sha256=P7fT_tXCzUuXATTkTx_DyD4EuG0_KjIDlay09MhkKTE,9824
|
|
46
|
+
mcp_vector_search/parsers/php.py,sha256=1QjnE8SAQF86VQ7pNfn1Pmpg5Dni4M7KCLU7212DkXM,24774
|
|
45
47
|
mcp_vector_search/parsers/python.py,sha256=IB4gQ6DD6Oqvws5p3LQtHTJOtmH6f9FWmO9fQRfGQx4,15688
|
|
46
|
-
mcp_vector_search/parsers/registry.py,sha256=
|
|
47
|
-
mcp_vector_search/parsers/
|
|
48
|
+
mcp_vector_search/parsers/registry.py,sha256=HN2v4p3UsUzplnKnOwuV89KfE70VZBc9ymB0Vypq9bU,6295
|
|
49
|
+
mcp_vector_search/parsers/ruby.py,sha256=xNn_z8txAWL7E1ULcFMiqn5idFhf5GQn8N3x1yE-c2k,23818
|
|
50
|
+
mcp_vector_search/parsers/text.py,sha256=dUxRzi-aoNsWP_fHRjkeCBZU0CMhnvBOwEuOHnPJKyc,5975
|
|
48
51
|
mcp_vector_search/utils/__init__.py,sha256=Eq6lY-oPMfCt-GpPUbg9QbmTHuQVmTaVDBMU2183KVw,887
|
|
49
|
-
mcp_vector_search/utils/gitignore.py,sha256=
|
|
52
|
+
mcp_vector_search/utils/gitignore.py,sha256=lc8_GK2L8dzlqPRZNB3XfQvVCbnLtz_l0eQN_13z8Xo,7684
|
|
50
53
|
mcp_vector_search/utils/timing.py,sha256=THC7mfbTYnUpnnDcblgQacYMzbEkfFoIShx6plmhCgg,11285
|
|
51
54
|
mcp_vector_search/utils/version.py,sha256=d7fS-CLemxb8UzZ9j18zH0Y0Ud097ljKKYYOPulnGPE,1138
|
|
52
|
-
mcp_vector_search-0.
|
|
53
|
-
mcp_vector_search-0.
|
|
54
|
-
mcp_vector_search-0.
|
|
55
|
-
mcp_vector_search-0.
|
|
56
|
-
mcp_vector_search-0.
|
|
55
|
+
mcp_vector_search-0.5.0.dist-info/METADATA,sha256=p_JJwOELJxBDp0AkQcizMKkk3nVNmUQKuZ1nKpD2SSw,18104
|
|
56
|
+
mcp_vector_search-0.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
57
|
+
mcp_vector_search-0.5.0.dist-info/entry_points.txt,sha256=y3Ygtc_JiBchNEIL-tPABo7EbzBExGAxwGdkkeP5D2I,86
|
|
58
|
+
mcp_vector_search-0.5.0.dist-info/licenses/LICENSE,sha256=FqZUgGJH_tZKZLQsMCpXaLawRyLmyFKRVfMwYyEcyTs,1072
|
|
59
|
+
mcp_vector_search-0.5.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|