mcp-vector-search 0.12.6__py3-none-any.whl → 1.1.22__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.
- mcp_vector_search/__init__.py +3 -3
- mcp_vector_search/analysis/__init__.py +111 -0
- mcp_vector_search/analysis/baseline/__init__.py +68 -0
- mcp_vector_search/analysis/baseline/comparator.py +462 -0
- mcp_vector_search/analysis/baseline/manager.py +621 -0
- mcp_vector_search/analysis/collectors/__init__.py +74 -0
- mcp_vector_search/analysis/collectors/base.py +164 -0
- mcp_vector_search/analysis/collectors/cohesion.py +463 -0
- mcp_vector_search/analysis/collectors/complexity.py +743 -0
- mcp_vector_search/analysis/collectors/coupling.py +1162 -0
- mcp_vector_search/analysis/collectors/halstead.py +514 -0
- mcp_vector_search/analysis/collectors/smells.py +325 -0
- mcp_vector_search/analysis/debt.py +516 -0
- mcp_vector_search/analysis/interpretation.py +685 -0
- mcp_vector_search/analysis/metrics.py +414 -0
- mcp_vector_search/analysis/reporters/__init__.py +7 -0
- mcp_vector_search/analysis/reporters/console.py +646 -0
- mcp_vector_search/analysis/reporters/markdown.py +480 -0
- mcp_vector_search/analysis/reporters/sarif.py +377 -0
- mcp_vector_search/analysis/storage/__init__.py +93 -0
- mcp_vector_search/analysis/storage/metrics_store.py +762 -0
- mcp_vector_search/analysis/storage/schema.py +245 -0
- mcp_vector_search/analysis/storage/trend_tracker.py +560 -0
- mcp_vector_search/analysis/trends.py +308 -0
- mcp_vector_search/analysis/visualizer/__init__.py +90 -0
- mcp_vector_search/analysis/visualizer/d3_data.py +534 -0
- mcp_vector_search/analysis/visualizer/exporter.py +484 -0
- mcp_vector_search/analysis/visualizer/html_report.py +2895 -0
- mcp_vector_search/analysis/visualizer/schemas.py +525 -0
- mcp_vector_search/cli/commands/analyze.py +1062 -0
- mcp_vector_search/cli/commands/chat.py +1455 -0
- mcp_vector_search/cli/commands/index.py +621 -5
- mcp_vector_search/cli/commands/index_background.py +467 -0
- mcp_vector_search/cli/commands/init.py +13 -0
- mcp_vector_search/cli/commands/install.py +597 -335
- mcp_vector_search/cli/commands/install_old.py +8 -4
- mcp_vector_search/cli/commands/mcp.py +78 -6
- mcp_vector_search/cli/commands/reset.py +68 -26
- mcp_vector_search/cli/commands/search.py +224 -8
- mcp_vector_search/cli/commands/setup.py +1184 -0
- mcp_vector_search/cli/commands/status.py +339 -5
- mcp_vector_search/cli/commands/uninstall.py +276 -357
- mcp_vector_search/cli/commands/visualize/__init__.py +39 -0
- mcp_vector_search/cli/commands/visualize/cli.py +292 -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 +33 -0
- mcp_vector_search/cli/commands/visualize/graph_builder.py +647 -0
- mcp_vector_search/cli/commands/visualize/layout_engine.py +469 -0
- mcp_vector_search/cli/commands/visualize/server.py +600 -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 +234 -0
- mcp_vector_search/cli/commands/visualize/templates/scripts.py +4542 -0
- mcp_vector_search/cli/commands/visualize/templates/styles.py +2522 -0
- mcp_vector_search/cli/didyoumean.py +27 -2
- mcp_vector_search/cli/main.py +127 -160
- mcp_vector_search/cli/output.py +158 -13
- mcp_vector_search/config/__init__.py +4 -0
- mcp_vector_search/config/default_thresholds.yaml +52 -0
- mcp_vector_search/config/settings.py +12 -0
- mcp_vector_search/config/thresholds.py +273 -0
- mcp_vector_search/core/__init__.py +16 -0
- mcp_vector_search/core/auto_indexer.py +3 -3
- mcp_vector_search/core/boilerplate.py +186 -0
- mcp_vector_search/core/config_utils.py +394 -0
- mcp_vector_search/core/database.py +406 -94
- mcp_vector_search/core/embeddings.py +24 -0
- mcp_vector_search/core/exceptions.py +11 -0
- mcp_vector_search/core/git.py +380 -0
- mcp_vector_search/core/git_hooks.py +4 -4
- mcp_vector_search/core/indexer.py +632 -54
- mcp_vector_search/core/llm_client.py +756 -0
- mcp_vector_search/core/models.py +91 -1
- mcp_vector_search/core/project.py +17 -0
- mcp_vector_search/core/relationships.py +473 -0
- mcp_vector_search/core/scheduler.py +11 -11
- mcp_vector_search/core/search.py +179 -29
- mcp_vector_search/mcp/server.py +819 -9
- mcp_vector_search/parsers/python.py +285 -5
- mcp_vector_search/utils/__init__.py +2 -0
- mcp_vector_search/utils/gitignore.py +0 -3
- mcp_vector_search/utils/gitignore_updater.py +212 -0
- mcp_vector_search/utils/monorepo.py +66 -4
- mcp_vector_search/utils/timing.py +10 -6
- {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.1.22.dist-info}/METADATA +184 -53
- mcp_vector_search-1.1.22.dist-info/RECORD +120 -0
- {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.1.22.dist-info}/WHEEL +1 -1
- {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.1.22.dist-info}/entry_points.txt +1 -0
- mcp_vector_search/cli/commands/visualize.py +0 -1467
- mcp_vector_search-0.12.6.dist-info/RECORD +0 -68
- {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.1.22.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Code Quality Threshold Configuration
|
|
2
|
+
# Customize these values to match your project's standards
|
|
3
|
+
|
|
4
|
+
complexity:
|
|
5
|
+
# Cognitive complexity grade thresholds
|
|
6
|
+
cognitive_a: 5 # Grade A: 0-5 (excellent)
|
|
7
|
+
cognitive_b: 10 # Grade B: 6-10 (good)
|
|
8
|
+
cognitive_c: 20 # Grade C: 11-20 (acceptable)
|
|
9
|
+
cognitive_d: 30 # Grade D: 21-30 (needs improvement)
|
|
10
|
+
# Grade F: 31+ (refactor recommended)
|
|
11
|
+
|
|
12
|
+
# Cyclomatic complexity thresholds
|
|
13
|
+
cyclomatic_low: 4 # Low: 1-4
|
|
14
|
+
cyclomatic_moderate: 10 # Moderate: 5-10
|
|
15
|
+
cyclomatic_high: 20 # High: 11-20
|
|
16
|
+
# Very high: 21+
|
|
17
|
+
|
|
18
|
+
# Nesting depth thresholds
|
|
19
|
+
nesting_warning: 3 # Warning level
|
|
20
|
+
nesting_error: 5 # Error level
|
|
21
|
+
|
|
22
|
+
# Parameter count thresholds
|
|
23
|
+
parameters_warning: 4 # Warning level
|
|
24
|
+
parameters_error: 7 # Error level
|
|
25
|
+
|
|
26
|
+
# Method count thresholds (per class)
|
|
27
|
+
methods_warning: 10 # Warning level
|
|
28
|
+
methods_error: 20 # Error level
|
|
29
|
+
|
|
30
|
+
smells:
|
|
31
|
+
# Long method (lines of code)
|
|
32
|
+
long_method_lines: 50
|
|
33
|
+
|
|
34
|
+
# Too many parameters
|
|
35
|
+
too_many_parameters: 5
|
|
36
|
+
|
|
37
|
+
# Deep nesting
|
|
38
|
+
deep_nesting_depth: 4
|
|
39
|
+
|
|
40
|
+
# High complexity
|
|
41
|
+
high_complexity: 15
|
|
42
|
+
|
|
43
|
+
# God class (too many methods)
|
|
44
|
+
god_class_methods: 20
|
|
45
|
+
|
|
46
|
+
# Feature envy (external calls)
|
|
47
|
+
feature_envy_external_calls: 5
|
|
48
|
+
|
|
49
|
+
# Quality gate settings
|
|
50
|
+
fail_on_f_grade: true # Fail quality gate on F grade
|
|
51
|
+
fail_on_smell_count: 10 # Fail if more than N smells total
|
|
52
|
+
warn_on_d_grade: true # Warn on D grade
|
|
@@ -49,6 +49,18 @@ class ProjectConfig(BaseSettings):
|
|
|
49
49
|
default=True,
|
|
50
50
|
description="Respect .gitignore patterns when indexing files",
|
|
51
51
|
)
|
|
52
|
+
openrouter_api_key: str | None = Field(
|
|
53
|
+
default=None,
|
|
54
|
+
description="OpenRouter API key for chat command (optional, can also use env var)",
|
|
55
|
+
)
|
|
56
|
+
openai_api_key: str | None = Field(
|
|
57
|
+
default=None,
|
|
58
|
+
description="OpenAI API key for chat command (optional, can also use env var)",
|
|
59
|
+
)
|
|
60
|
+
preferred_llm_provider: str | None = Field(
|
|
61
|
+
default=None,
|
|
62
|
+
description="Preferred LLM provider: 'openai' or 'openrouter' (auto-detect if not set)",
|
|
63
|
+
)
|
|
52
64
|
|
|
53
65
|
@field_validator("project_root", "index_path", mode="before")
|
|
54
66
|
@classmethod
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""Threshold configuration for code quality metrics."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import yaml
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class ComplexityThresholds:
|
|
14
|
+
"""Thresholds for complexity metrics."""
|
|
15
|
+
|
|
16
|
+
# Cognitive complexity thresholds for grades
|
|
17
|
+
cognitive_a: int = 5 # A grade: 0-5
|
|
18
|
+
cognitive_b: int = 10 # B grade: 6-10
|
|
19
|
+
cognitive_c: int = 20 # C grade: 11-20
|
|
20
|
+
cognitive_d: int = 30 # D grade: 21-30
|
|
21
|
+
# F grade: 31+
|
|
22
|
+
|
|
23
|
+
# Cyclomatic complexity thresholds
|
|
24
|
+
cyclomatic_low: int = 4 # Low complexity
|
|
25
|
+
cyclomatic_moderate: int = 10 # Moderate
|
|
26
|
+
cyclomatic_high: int = 20 # High (needs attention)
|
|
27
|
+
# Very high: 21+
|
|
28
|
+
|
|
29
|
+
# Nesting depth thresholds
|
|
30
|
+
nesting_warning: int = 3 # Warning level
|
|
31
|
+
nesting_error: int = 5 # Error level
|
|
32
|
+
|
|
33
|
+
# Parameter count thresholds
|
|
34
|
+
parameters_warning: int = 4 # Warning level
|
|
35
|
+
parameters_error: int = 7 # Error level
|
|
36
|
+
|
|
37
|
+
# Method count thresholds
|
|
38
|
+
methods_warning: int = 10 # Warning level
|
|
39
|
+
methods_error: int = 20 # Error level
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class SmellThresholds:
|
|
44
|
+
"""Thresholds for code smell detection."""
|
|
45
|
+
|
|
46
|
+
# Long method threshold (lines of code)
|
|
47
|
+
long_method_lines: int = 50
|
|
48
|
+
|
|
49
|
+
# Too many parameters
|
|
50
|
+
too_many_parameters: int = 5
|
|
51
|
+
|
|
52
|
+
# Deep nesting
|
|
53
|
+
deep_nesting_depth: int = 4
|
|
54
|
+
|
|
55
|
+
# High complexity
|
|
56
|
+
high_complexity: int = 15
|
|
57
|
+
|
|
58
|
+
# God class (too many methods and lines)
|
|
59
|
+
god_class_methods: int = 20
|
|
60
|
+
god_class_lines: int = 500
|
|
61
|
+
|
|
62
|
+
# Feature envy (placeholder for future)
|
|
63
|
+
feature_envy_external_calls: int = 5
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass
|
|
67
|
+
class CouplingThresholds:
|
|
68
|
+
"""Thresholds for coupling and instability metrics."""
|
|
69
|
+
|
|
70
|
+
# Efferent coupling (Ce) thresholds
|
|
71
|
+
efferent_low: int = 3 # Low coupling (0-3 dependencies)
|
|
72
|
+
efferent_moderate: int = 7 # Moderate coupling (4-7)
|
|
73
|
+
efferent_high: int = 12 # High coupling (8-12)
|
|
74
|
+
# Very high: 13+
|
|
75
|
+
|
|
76
|
+
# Afferent coupling (Ca) thresholds
|
|
77
|
+
afferent_low: int = 2 # Low coupling (0-2 dependents)
|
|
78
|
+
afferent_moderate: int = 5 # Moderate coupling (3-5)
|
|
79
|
+
afferent_high: int = 10 # High coupling (6-10)
|
|
80
|
+
# Very high: 11+
|
|
81
|
+
|
|
82
|
+
# Instability (I) thresholds for grades
|
|
83
|
+
instability_a: float = 0.2 # A grade: very stable (0.0-0.2)
|
|
84
|
+
instability_b: float = 0.4 # B grade: stable (0.2-0.4)
|
|
85
|
+
instability_c: float = 0.6 # C grade: balanced (0.4-0.6)
|
|
86
|
+
instability_d: float = 0.8 # D grade: unstable (0.6-0.8)
|
|
87
|
+
# F grade: very unstable (0.8-1.0)
|
|
88
|
+
|
|
89
|
+
# Category thresholds
|
|
90
|
+
stable_max: float = 0.3 # Stable category (0.0-0.3)
|
|
91
|
+
balanced_max: float = 0.7 # Balanced category (0.3-0.7)
|
|
92
|
+
# Unstable category: 0.7-1.0
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass
|
|
96
|
+
class ThresholdConfig:
|
|
97
|
+
"""Complete threshold configuration."""
|
|
98
|
+
|
|
99
|
+
complexity: ComplexityThresholds = field(default_factory=ComplexityThresholds)
|
|
100
|
+
smells: SmellThresholds = field(default_factory=SmellThresholds)
|
|
101
|
+
coupling: CouplingThresholds = field(default_factory=CouplingThresholds)
|
|
102
|
+
|
|
103
|
+
# Quality gate settings
|
|
104
|
+
fail_on_f_grade: bool = True
|
|
105
|
+
fail_on_smell_count: int = 10 # Fail if more than N smells
|
|
106
|
+
warn_on_d_grade: bool = True
|
|
107
|
+
|
|
108
|
+
@classmethod
|
|
109
|
+
def load(cls, path: Path) -> ThresholdConfig:
|
|
110
|
+
"""Load configuration from YAML file.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
path: Path to YAML configuration file
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
ThresholdConfig instance
|
|
117
|
+
"""
|
|
118
|
+
if not path.exists():
|
|
119
|
+
return cls()
|
|
120
|
+
|
|
121
|
+
with open(path) as f:
|
|
122
|
+
data = yaml.safe_load(f) or {}
|
|
123
|
+
|
|
124
|
+
return cls.from_dict(data)
|
|
125
|
+
|
|
126
|
+
@classmethod
|
|
127
|
+
def from_dict(cls, data: dict[str, Any]) -> ThresholdConfig:
|
|
128
|
+
"""Create config from dictionary.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
data: Configuration dictionary
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
ThresholdConfig instance
|
|
135
|
+
"""
|
|
136
|
+
complexity_data = data.get("complexity", {})
|
|
137
|
+
smells_data = data.get("smells", {})
|
|
138
|
+
coupling_data = data.get("coupling", {})
|
|
139
|
+
|
|
140
|
+
return cls(
|
|
141
|
+
complexity=(
|
|
142
|
+
ComplexityThresholds(**complexity_data)
|
|
143
|
+
if complexity_data
|
|
144
|
+
else ComplexityThresholds()
|
|
145
|
+
),
|
|
146
|
+
smells=(
|
|
147
|
+
SmellThresholds(**smells_data) if smells_data else SmellThresholds()
|
|
148
|
+
),
|
|
149
|
+
coupling=(
|
|
150
|
+
CouplingThresholds(**coupling_data)
|
|
151
|
+
if coupling_data
|
|
152
|
+
else CouplingThresholds()
|
|
153
|
+
),
|
|
154
|
+
fail_on_f_grade=data.get("fail_on_f_grade", True),
|
|
155
|
+
fail_on_smell_count=data.get("fail_on_smell_count", 10),
|
|
156
|
+
warn_on_d_grade=data.get("warn_on_d_grade", True),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
def to_dict(self) -> dict[str, Any]:
|
|
160
|
+
"""Convert to dictionary for serialization.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Dictionary representation
|
|
164
|
+
"""
|
|
165
|
+
return {
|
|
166
|
+
"complexity": {
|
|
167
|
+
"cognitive_a": self.complexity.cognitive_a,
|
|
168
|
+
"cognitive_b": self.complexity.cognitive_b,
|
|
169
|
+
"cognitive_c": self.complexity.cognitive_c,
|
|
170
|
+
"cognitive_d": self.complexity.cognitive_d,
|
|
171
|
+
"cyclomatic_low": self.complexity.cyclomatic_low,
|
|
172
|
+
"cyclomatic_moderate": self.complexity.cyclomatic_moderate,
|
|
173
|
+
"cyclomatic_high": self.complexity.cyclomatic_high,
|
|
174
|
+
"nesting_warning": self.complexity.nesting_warning,
|
|
175
|
+
"nesting_error": self.complexity.nesting_error,
|
|
176
|
+
"parameters_warning": self.complexity.parameters_warning,
|
|
177
|
+
"parameters_error": self.complexity.parameters_error,
|
|
178
|
+
"methods_warning": self.complexity.methods_warning,
|
|
179
|
+
"methods_error": self.complexity.methods_error,
|
|
180
|
+
},
|
|
181
|
+
"smells": {
|
|
182
|
+
"long_method_lines": self.smells.long_method_lines,
|
|
183
|
+
"too_many_parameters": self.smells.too_many_parameters,
|
|
184
|
+
"deep_nesting_depth": self.smells.deep_nesting_depth,
|
|
185
|
+
"high_complexity": self.smells.high_complexity,
|
|
186
|
+
"god_class_methods": self.smells.god_class_methods,
|
|
187
|
+
"god_class_lines": self.smells.god_class_lines,
|
|
188
|
+
"feature_envy_external_calls": self.smells.feature_envy_external_calls,
|
|
189
|
+
},
|
|
190
|
+
"coupling": {
|
|
191
|
+
"efferent_low": self.coupling.efferent_low,
|
|
192
|
+
"efferent_moderate": self.coupling.efferent_moderate,
|
|
193
|
+
"efferent_high": self.coupling.efferent_high,
|
|
194
|
+
"afferent_low": self.coupling.afferent_low,
|
|
195
|
+
"afferent_moderate": self.coupling.afferent_moderate,
|
|
196
|
+
"afferent_high": self.coupling.afferent_high,
|
|
197
|
+
"instability_a": self.coupling.instability_a,
|
|
198
|
+
"instability_b": self.coupling.instability_b,
|
|
199
|
+
"instability_c": self.coupling.instability_c,
|
|
200
|
+
"instability_d": self.coupling.instability_d,
|
|
201
|
+
"stable_max": self.coupling.stable_max,
|
|
202
|
+
"balanced_max": self.coupling.balanced_max,
|
|
203
|
+
},
|
|
204
|
+
"fail_on_f_grade": self.fail_on_f_grade,
|
|
205
|
+
"fail_on_smell_count": self.fail_on_smell_count,
|
|
206
|
+
"warn_on_d_grade": self.warn_on_d_grade,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
def save(self, path: Path) -> None:
|
|
210
|
+
"""Save configuration to YAML file.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
path: Path to save configuration
|
|
214
|
+
"""
|
|
215
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
216
|
+
with open(path, "w") as f:
|
|
217
|
+
yaml.dump(self.to_dict(), f, default_flow_style=False, sort_keys=False)
|
|
218
|
+
|
|
219
|
+
def get_grade(self, cognitive_complexity: int) -> str:
|
|
220
|
+
"""Get complexity grade based on cognitive complexity.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
cognitive_complexity: Cognitive complexity value
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Grade from A to F
|
|
227
|
+
"""
|
|
228
|
+
if cognitive_complexity <= self.complexity.cognitive_a:
|
|
229
|
+
return "A"
|
|
230
|
+
elif cognitive_complexity <= self.complexity.cognitive_b:
|
|
231
|
+
return "B"
|
|
232
|
+
elif cognitive_complexity <= self.complexity.cognitive_c:
|
|
233
|
+
return "C"
|
|
234
|
+
elif cognitive_complexity <= self.complexity.cognitive_d:
|
|
235
|
+
return "D"
|
|
236
|
+
else:
|
|
237
|
+
return "F"
|
|
238
|
+
|
|
239
|
+
def get_instability_grade(self, instability: float) -> str:
|
|
240
|
+
"""Get instability grade based on instability value.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
instability: Instability value (0.0-1.0)
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Grade from A to F
|
|
247
|
+
"""
|
|
248
|
+
if instability <= self.coupling.instability_a:
|
|
249
|
+
return "A"
|
|
250
|
+
elif instability <= self.coupling.instability_b:
|
|
251
|
+
return "B"
|
|
252
|
+
elif instability <= self.coupling.instability_c:
|
|
253
|
+
return "C"
|
|
254
|
+
elif instability <= self.coupling.instability_d:
|
|
255
|
+
return "D"
|
|
256
|
+
else:
|
|
257
|
+
return "F"
|
|
258
|
+
|
|
259
|
+
def get_stability_category(self, instability: float) -> str:
|
|
260
|
+
"""Get stability category based on instability value.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
instability: Instability value (0.0-1.0)
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
Category: "Stable", "Balanced", or "Unstable"
|
|
267
|
+
"""
|
|
268
|
+
if instability <= self.coupling.stable_max:
|
|
269
|
+
return "Stable"
|
|
270
|
+
elif instability <= self.coupling.balanced_max:
|
|
271
|
+
return "Balanced"
|
|
272
|
+
else:
|
|
273
|
+
return "Unstable"
|
|
@@ -1 +1,17 @@
|
|
|
1
1
|
"""Core functionality for MCP Vector Search."""
|
|
2
|
+
|
|
3
|
+
from mcp_vector_search.core.git import (
|
|
4
|
+
GitError,
|
|
5
|
+
GitManager,
|
|
6
|
+
GitNotAvailableError,
|
|
7
|
+
GitNotRepoError,
|
|
8
|
+
GitReferenceError,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"GitError",
|
|
13
|
+
"GitManager",
|
|
14
|
+
"GitNotAvailableError",
|
|
15
|
+
"GitNotRepoError",
|
|
16
|
+
"GitReferenceError",
|
|
17
|
+
]
|
|
@@ -192,9 +192,9 @@ class AutoIndexer:
|
|
|
192
192
|
"staleness_seconds": staleness_seconds,
|
|
193
193
|
"is_stale": staleness_seconds > self.staleness_threshold,
|
|
194
194
|
"newest_file_time": newest_file_time,
|
|
195
|
-
"oldest_index_time":
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
"oldest_index_time": (
|
|
196
|
+
oldest_index_time if oldest_index_time != float("inf") else 0
|
|
197
|
+
),
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
except Exception as e:
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Boilerplate filtering for semantic search results.
|
|
2
|
+
|
|
3
|
+
This module provides language-specific filtering to penalize common boilerplate
|
|
4
|
+
code patterns (constructors, lifecycle methods, etc.) in search results while
|
|
5
|
+
still preserving them when explicitly queried.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Final
|
|
9
|
+
|
|
10
|
+
# Language-specific boilerplate function/method names
|
|
11
|
+
# Using frozensets for O(1) lookup performance
|
|
12
|
+
_PYTHON_BOILERPLATE: Final[frozenset[str]] = frozenset(
|
|
13
|
+
{
|
|
14
|
+
"__init__",
|
|
15
|
+
"__str__",
|
|
16
|
+
"__repr__",
|
|
17
|
+
"__eq__",
|
|
18
|
+
"__hash__",
|
|
19
|
+
"__len__",
|
|
20
|
+
"__iter__",
|
|
21
|
+
"__next__",
|
|
22
|
+
"__enter__",
|
|
23
|
+
"__exit__",
|
|
24
|
+
"main",
|
|
25
|
+
"setUp",
|
|
26
|
+
"tearDown",
|
|
27
|
+
"setUpClass",
|
|
28
|
+
"tearDownClass",
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
_JAVASCRIPT_TYPESCRIPT_BOILERPLATE: Final[frozenset[str]] = frozenset(
|
|
33
|
+
{
|
|
34
|
+
"constructor",
|
|
35
|
+
"render",
|
|
36
|
+
"componentDidMount",
|
|
37
|
+
"componentWillUnmount",
|
|
38
|
+
"componentDidUpdate",
|
|
39
|
+
"useState",
|
|
40
|
+
"useEffect",
|
|
41
|
+
"index",
|
|
42
|
+
"main",
|
|
43
|
+
"default",
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
_DART_BOILERPLATE: Final[frozenset[str]] = frozenset(
|
|
48
|
+
{
|
|
49
|
+
"build",
|
|
50
|
+
"dispose",
|
|
51
|
+
"initState",
|
|
52
|
+
"didChangeDependencies",
|
|
53
|
+
"main",
|
|
54
|
+
"createState",
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
_PHP_BOILERPLATE: Final[frozenset[str]] = frozenset(
|
|
59
|
+
{
|
|
60
|
+
"__construct",
|
|
61
|
+
"__destruct",
|
|
62
|
+
"__toString",
|
|
63
|
+
"__get",
|
|
64
|
+
"__set",
|
|
65
|
+
"__call",
|
|
66
|
+
"__callStatic",
|
|
67
|
+
"index",
|
|
68
|
+
"main",
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
_RUBY_BOILERPLATE: Final[frozenset[str]] = frozenset(
|
|
73
|
+
{
|
|
74
|
+
"initialize",
|
|
75
|
+
"to_s",
|
|
76
|
+
"to_h",
|
|
77
|
+
"to_a",
|
|
78
|
+
"inspect",
|
|
79
|
+
"main",
|
|
80
|
+
"setup",
|
|
81
|
+
"teardown",
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class BoilerplateFilter:
|
|
87
|
+
"""Filter for identifying and penalizing boilerplate code patterns.
|
|
88
|
+
|
|
89
|
+
This filter applies language-specific penalties to common boilerplate
|
|
90
|
+
patterns (constructors, lifecycle methods, etc.) to improve search
|
|
91
|
+
result relevance. It avoids filtering when the user explicitly searches
|
|
92
|
+
for a boilerplate pattern.
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
filter = BoilerplateFilter()
|
|
96
|
+
|
|
97
|
+
# Returns penalty for __init__ in Python
|
|
98
|
+
penalty = filter.get_penalty("__init__", "python", "search classes")
|
|
99
|
+
|
|
100
|
+
# Returns 0.0 when explicitly searching for __init__
|
|
101
|
+
penalty = filter.get_penalty("__init__", "python", "find __init__ methods")
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
# Default penalty for boilerplate patterns
|
|
105
|
+
DEFAULT_PENALTY: Final[float] = -0.15
|
|
106
|
+
|
|
107
|
+
# Mapping of language identifiers to boilerplate sets
|
|
108
|
+
_LANGUAGE_BOILERPLATE: Final[dict[str, frozenset[str]]] = {
|
|
109
|
+
"python": _PYTHON_BOILERPLATE,
|
|
110
|
+
"javascript": _JAVASCRIPT_TYPESCRIPT_BOILERPLATE,
|
|
111
|
+
"typescript": _JAVASCRIPT_TYPESCRIPT_BOILERPLATE,
|
|
112
|
+
"jsx": _JAVASCRIPT_TYPESCRIPT_BOILERPLATE,
|
|
113
|
+
"tsx": _JAVASCRIPT_TYPESCRIPT_BOILERPLATE,
|
|
114
|
+
"dart": _DART_BOILERPLATE,
|
|
115
|
+
"php": _PHP_BOILERPLATE,
|
|
116
|
+
"ruby": _RUBY_BOILERPLATE,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
def is_boilerplate(self, name: str, language: str, query: str) -> bool:
|
|
120
|
+
"""Check if a function/method name is considered boilerplate.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
name: Function or method name to check
|
|
124
|
+
language: Programming language (e.g., "python", "javascript")
|
|
125
|
+
query: Original search query
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
True if the name is boilerplate AND not explicitly in the query
|
|
129
|
+
|
|
130
|
+
Example:
|
|
131
|
+
>>> filter = BoilerplateFilter()
|
|
132
|
+
>>> filter.is_boilerplate("__init__", "python", "find classes")
|
|
133
|
+
True
|
|
134
|
+
>>> filter.is_boilerplate("__init__", "python", "show __init__ methods")
|
|
135
|
+
False
|
|
136
|
+
"""
|
|
137
|
+
if not name:
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
# Don't filter if user explicitly searched for this boilerplate
|
|
141
|
+
query_lower = query.lower()
|
|
142
|
+
name_lower = name.lower()
|
|
143
|
+
if name_lower in query_lower:
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
# Check if name is in language's boilerplate set
|
|
147
|
+
language_lower = language.lower()
|
|
148
|
+
boilerplate_set = self._LANGUAGE_BOILERPLATE.get(language_lower)
|
|
149
|
+
|
|
150
|
+
if boilerplate_set is None:
|
|
151
|
+
# Unknown language - no filtering
|
|
152
|
+
return False
|
|
153
|
+
|
|
154
|
+
# Exact match (case-sensitive for languages like Python)
|
|
155
|
+
return name in boilerplate_set
|
|
156
|
+
|
|
157
|
+
def get_penalty(
|
|
158
|
+
self,
|
|
159
|
+
name: str,
|
|
160
|
+
language: str,
|
|
161
|
+
query: str = "",
|
|
162
|
+
penalty: float | None = None,
|
|
163
|
+
) -> float:
|
|
164
|
+
"""Calculate penalty for a potentially boilerplate name.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
name: Function or method name to check
|
|
168
|
+
language: Programming language (e.g., "python", "javascript")
|
|
169
|
+
query: Original search query (default: "")
|
|
170
|
+
penalty: Custom penalty value (default: uses DEFAULT_PENALTY)
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Penalty value (negative float) if boilerplate, 0.0 otherwise
|
|
174
|
+
|
|
175
|
+
Example:
|
|
176
|
+
>>> filter = BoilerplateFilter()
|
|
177
|
+
>>> filter.get_penalty("__init__", "python", "search classes")
|
|
178
|
+
-0.15
|
|
179
|
+
>>> filter.get_penalty("__init__", "python", "find __init__")
|
|
180
|
+
0.0
|
|
181
|
+
>>> filter.get_penalty("custom_method", "python", "search")
|
|
182
|
+
0.0
|
|
183
|
+
"""
|
|
184
|
+
if self.is_boilerplate(name, language, query):
|
|
185
|
+
return penalty if penalty is not None else self.DEFAULT_PENALTY
|
|
186
|
+
return 0.0
|