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,330 @@
|
|
|
1
|
+
"""Scheduling utilities for automatic reindexing."""
|
|
2
|
+
|
|
3
|
+
import platform
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from loguru import logger
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SchedulerManager:
|
|
12
|
+
"""Manages scheduled tasks for automatic reindexing."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, project_root: Path):
|
|
15
|
+
"""Initialize scheduler manager.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
project_root: Project root directory
|
|
19
|
+
"""
|
|
20
|
+
self.project_root = project_root
|
|
21
|
+
self.system = platform.system().lower()
|
|
22
|
+
|
|
23
|
+
def install_scheduled_task(
|
|
24
|
+
self, interval_minutes: int = 60, task_name: str | None = None
|
|
25
|
+
) -> bool:
|
|
26
|
+
"""Install a scheduled task for automatic reindexing.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
interval_minutes: Interval between reindex checks in minutes
|
|
30
|
+
task_name: Custom task name (auto-generated if None)
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
True if task was installed successfully
|
|
34
|
+
"""
|
|
35
|
+
if task_name is None:
|
|
36
|
+
safe_path = str(self.project_root).replace("/", "_").replace("\\", "_")
|
|
37
|
+
task_name = f"mcp_vector_search_reindex_{safe_path}"
|
|
38
|
+
|
|
39
|
+
if self.system == "linux" or self.system == "darwin":
|
|
40
|
+
return self._install_cron_job(interval_minutes, task_name)
|
|
41
|
+
elif self.system == "windows":
|
|
42
|
+
return self._install_windows_task(interval_minutes, task_name)
|
|
43
|
+
else:
|
|
44
|
+
logger.error(f"Unsupported system: {self.system}")
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
def uninstall_scheduled_task(self, task_name: str | None = None) -> bool:
|
|
48
|
+
"""Uninstall scheduled task.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
task_name: Task name to uninstall (auto-generated if None)
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
True if task was uninstalled successfully
|
|
55
|
+
"""
|
|
56
|
+
if task_name is None:
|
|
57
|
+
safe_path = str(self.project_root).replace("/", "_").replace("\\", "_")
|
|
58
|
+
task_name = f"mcp_vector_search_reindex_{safe_path}"
|
|
59
|
+
|
|
60
|
+
if self.system == "linux" or self.system == "darwin":
|
|
61
|
+
return self._uninstall_cron_job(task_name)
|
|
62
|
+
elif self.system == "windows":
|
|
63
|
+
return self._uninstall_windows_task(task_name)
|
|
64
|
+
else:
|
|
65
|
+
logger.error(f"Unsupported system: {self.system}")
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
def _install_cron_job(self, interval_minutes: int, task_name: str) -> bool:
|
|
69
|
+
"""Install cron job on Linux/macOS."""
|
|
70
|
+
try:
|
|
71
|
+
# Generate cron command
|
|
72
|
+
python_path = sys.executable
|
|
73
|
+
project_root = str(self.project_root)
|
|
74
|
+
|
|
75
|
+
# Create wrapper script
|
|
76
|
+
script_content = f"""#!/bin/bash
|
|
77
|
+
# MCP Vector Search Auto-Reindex - {task_name}
|
|
78
|
+
cd "{project_root}" || exit 1
|
|
79
|
+
|
|
80
|
+
# Check if mcp-vector-search is available
|
|
81
|
+
if command -v mcp-vector-search &> /dev/null; then
|
|
82
|
+
mcp-vector-search auto-index check --auto-reindex --max-files 10
|
|
83
|
+
elif [ -f "{python_path}" ]; then
|
|
84
|
+
"{python_path}" -m mcp_vector_search auto-index check --auto-reindex --max-files 10
|
|
85
|
+
else
|
|
86
|
+
python3 -m mcp_vector_search auto-index check --auto-reindex --max-files 10
|
|
87
|
+
fi
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
# Write script to temp file
|
|
91
|
+
script_dir = Path.home() / ".mcp-vector-search" / "scripts"
|
|
92
|
+
script_dir.mkdir(parents=True, exist_ok=True)
|
|
93
|
+
script_file = script_dir / f"{task_name}.sh"
|
|
94
|
+
|
|
95
|
+
script_file.write_text(script_content)
|
|
96
|
+
script_file.chmod(0o755)
|
|
97
|
+
|
|
98
|
+
# Calculate cron schedule
|
|
99
|
+
if interval_minutes >= 60:
|
|
100
|
+
# Hourly or less frequent
|
|
101
|
+
hours = interval_minutes // 60
|
|
102
|
+
cron_schedule = f"0 */{hours} * * *"
|
|
103
|
+
else:
|
|
104
|
+
# More frequent than hourly
|
|
105
|
+
cron_schedule = f"*/{interval_minutes} * * * *"
|
|
106
|
+
|
|
107
|
+
# Add to crontab
|
|
108
|
+
cron_entry = f"{cron_schedule} {script_file} # {task_name}\n"
|
|
109
|
+
|
|
110
|
+
# Get current crontab
|
|
111
|
+
try:
|
|
112
|
+
result = subprocess.run( # nosec B607
|
|
113
|
+
["crontab", "-l"], capture_output=True, text=True, check=True
|
|
114
|
+
)
|
|
115
|
+
current_crontab = result.stdout
|
|
116
|
+
except subprocess.CalledProcessError:
|
|
117
|
+
current_crontab = ""
|
|
118
|
+
|
|
119
|
+
# Check if entry already exists
|
|
120
|
+
if task_name in current_crontab:
|
|
121
|
+
logger.info(f"Cron job {task_name} already exists")
|
|
122
|
+
return True
|
|
123
|
+
|
|
124
|
+
# Add new entry
|
|
125
|
+
new_crontab = current_crontab + cron_entry
|
|
126
|
+
|
|
127
|
+
# Install new crontab
|
|
128
|
+
process = subprocess.Popen( # nosec B607
|
|
129
|
+
["crontab", "-"], stdin=subprocess.PIPE, text=True
|
|
130
|
+
)
|
|
131
|
+
process.communicate(input=new_crontab)
|
|
132
|
+
|
|
133
|
+
if process.returncode == 0:
|
|
134
|
+
logger.info(f"Installed cron job: {task_name}")
|
|
135
|
+
logger.info(f"Schedule: every {interval_minutes} minutes")
|
|
136
|
+
logger.info(f"Script: {script_file}")
|
|
137
|
+
return True
|
|
138
|
+
else:
|
|
139
|
+
logger.error("Failed to install cron job")
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
except Exception as e:
|
|
143
|
+
logger.error(f"Failed to install cron job: {e}")
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
def _uninstall_cron_job(self, task_name: str) -> bool:
|
|
147
|
+
"""Uninstall cron job on Linux/macOS."""
|
|
148
|
+
try:
|
|
149
|
+
# Get current crontab
|
|
150
|
+
try:
|
|
151
|
+
result = subprocess.run( # nosec B607
|
|
152
|
+
["crontab", "-l"], capture_output=True, text=True, check=True
|
|
153
|
+
)
|
|
154
|
+
current_crontab = result.stdout
|
|
155
|
+
except subprocess.CalledProcessError:
|
|
156
|
+
logger.info("No crontab found")
|
|
157
|
+
return True
|
|
158
|
+
|
|
159
|
+
# Remove lines containing task name
|
|
160
|
+
lines = current_crontab.split("\n")
|
|
161
|
+
new_lines = [line for line in lines if task_name not in line]
|
|
162
|
+
new_crontab = "\n".join(new_lines)
|
|
163
|
+
|
|
164
|
+
# Install new crontab
|
|
165
|
+
if new_crontab.strip():
|
|
166
|
+
process = subprocess.Popen( # nosec B607
|
|
167
|
+
["crontab", "-"], stdin=subprocess.PIPE, text=True
|
|
168
|
+
)
|
|
169
|
+
process.communicate(input=new_crontab)
|
|
170
|
+
else:
|
|
171
|
+
# Remove crontab entirely if empty
|
|
172
|
+
subprocess.run(["crontab", "-r"], check=False) # nosec B607
|
|
173
|
+
|
|
174
|
+
# Remove script file
|
|
175
|
+
script_dir = Path.home() / ".mcp-vector-search" / "scripts"
|
|
176
|
+
script_file = script_dir / f"{task_name}.sh"
|
|
177
|
+
if script_file.exists():
|
|
178
|
+
script_file.unlink()
|
|
179
|
+
|
|
180
|
+
logger.info(f"Uninstalled cron job: {task_name}")
|
|
181
|
+
return True
|
|
182
|
+
|
|
183
|
+
except Exception as e:
|
|
184
|
+
logger.error(f"Failed to uninstall cron job: {e}")
|
|
185
|
+
return False
|
|
186
|
+
|
|
187
|
+
def _install_windows_task(self, interval_minutes: int, task_name: str) -> bool:
|
|
188
|
+
"""Install Windows scheduled task."""
|
|
189
|
+
try:
|
|
190
|
+
python_path = sys.executable
|
|
191
|
+
project_root = str(self.project_root)
|
|
192
|
+
|
|
193
|
+
# Create PowerShell script
|
|
194
|
+
script_content = f"""# MCP Vector Search Auto-Reindex - {task_name}
|
|
195
|
+
Set-Location "{project_root}"
|
|
196
|
+
|
|
197
|
+
try {{
|
|
198
|
+
if (Get-Command "mcp-vector-search" -ErrorAction SilentlyContinue) {{
|
|
199
|
+
mcp-vector-search auto-index check --auto-reindex --max-files 10
|
|
200
|
+
}} elseif (Test-Path "{python_path}") {{
|
|
201
|
+
& "{python_path}" -m mcp_vector_search auto-index check --auto-reindex --max-files 10
|
|
202
|
+
}} else {{
|
|
203
|
+
python -m mcp_vector_search auto-index check --auto-reindex --max-files 10
|
|
204
|
+
}}
|
|
205
|
+
}} catch {{
|
|
206
|
+
# Silently ignore errors
|
|
207
|
+
}}
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
# Write script
|
|
211
|
+
script_dir = Path.home() / ".mcp-vector-search" / "scripts"
|
|
212
|
+
script_dir.mkdir(parents=True, exist_ok=True)
|
|
213
|
+
script_file = script_dir / f"{task_name}.ps1"
|
|
214
|
+
|
|
215
|
+
script_file.write_text(script_content)
|
|
216
|
+
|
|
217
|
+
# Create scheduled task using schtasks
|
|
218
|
+
cmd = [
|
|
219
|
+
"schtasks",
|
|
220
|
+
"/create",
|
|
221
|
+
"/tn",
|
|
222
|
+
task_name,
|
|
223
|
+
"/tr",
|
|
224
|
+
f'powershell.exe -ExecutionPolicy Bypass -File "{script_file}"',
|
|
225
|
+
"/sc",
|
|
226
|
+
"minute",
|
|
227
|
+
"/mo",
|
|
228
|
+
str(interval_minutes),
|
|
229
|
+
"/f", # Force overwrite if exists
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
233
|
+
|
|
234
|
+
if result.returncode == 0:
|
|
235
|
+
logger.info(f"Installed Windows task: {task_name}")
|
|
236
|
+
logger.info(f"Schedule: every {interval_minutes} minutes")
|
|
237
|
+
return True
|
|
238
|
+
else:
|
|
239
|
+
logger.error(f"Failed to install Windows task: {result.stderr}")
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
except Exception as e:
|
|
243
|
+
logger.error(f"Failed to install Windows task: {e}")
|
|
244
|
+
return False
|
|
245
|
+
|
|
246
|
+
def _uninstall_windows_task(self, task_name: str) -> bool:
|
|
247
|
+
"""Uninstall Windows scheduled task."""
|
|
248
|
+
try:
|
|
249
|
+
# Delete scheduled task
|
|
250
|
+
cmd = ["schtasks", "/delete", "/tn", task_name, "/f"]
|
|
251
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
252
|
+
|
|
253
|
+
# Remove script file
|
|
254
|
+
script_dir = Path.home() / ".mcp-vector-search" / "scripts"
|
|
255
|
+
script_file = script_dir / f"{task_name}.ps1"
|
|
256
|
+
if script_file.exists():
|
|
257
|
+
script_file.unlink()
|
|
258
|
+
|
|
259
|
+
if result.returncode == 0:
|
|
260
|
+
logger.info(f"Uninstalled Windows task: {task_name}")
|
|
261
|
+
return True
|
|
262
|
+
else:
|
|
263
|
+
# Task might not exist, which is fine
|
|
264
|
+
logger.info(
|
|
265
|
+
f"Windows task {task_name} was not found (already uninstalled)"
|
|
266
|
+
)
|
|
267
|
+
return True
|
|
268
|
+
|
|
269
|
+
except Exception as e:
|
|
270
|
+
logger.error(f"Failed to uninstall Windows task: {e}")
|
|
271
|
+
return False
|
|
272
|
+
|
|
273
|
+
def get_scheduled_task_status(self, task_name: str | None = None) -> dict:
|
|
274
|
+
"""Get status of scheduled tasks.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
task_name: Task name to check (auto-generated if None)
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Dictionary with task status information
|
|
281
|
+
"""
|
|
282
|
+
if task_name is None:
|
|
283
|
+
safe_path = str(self.project_root).replace("/", "_").replace("\\", "_")
|
|
284
|
+
task_name = f"mcp_vector_search_reindex_{safe_path}"
|
|
285
|
+
|
|
286
|
+
status = {
|
|
287
|
+
"system": self.system,
|
|
288
|
+
"task_name": task_name,
|
|
289
|
+
"exists": False,
|
|
290
|
+
"enabled": False,
|
|
291
|
+
"last_run": None,
|
|
292
|
+
"next_run": None,
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if self.system == "linux" or self.system == "darwin":
|
|
296
|
+
status.update(self._get_cron_status(task_name))
|
|
297
|
+
elif self.system == "windows":
|
|
298
|
+
status.update(self._get_windows_task_status(task_name))
|
|
299
|
+
|
|
300
|
+
return status
|
|
301
|
+
|
|
302
|
+
def _get_cron_status(self, task_name: str) -> dict:
|
|
303
|
+
"""Get cron job status."""
|
|
304
|
+
try:
|
|
305
|
+
result = subprocess.run( # nosec B607
|
|
306
|
+
["crontab", "-l"], capture_output=True, text=True, check=True
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
exists = task_name in result.stdout
|
|
310
|
+
return {"exists": exists, "enabled": exists}
|
|
311
|
+
|
|
312
|
+
except subprocess.CalledProcessError:
|
|
313
|
+
return {"exists": False, "enabled": False}
|
|
314
|
+
|
|
315
|
+
def _get_windows_task_status(self, task_name: str) -> dict:
|
|
316
|
+
"""Get Windows task status."""
|
|
317
|
+
try:
|
|
318
|
+
result = subprocess.run( # nosec B607
|
|
319
|
+
["schtasks", "/query", "/tn", task_name], capture_output=True, text=True
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
if result.returncode == 0:
|
|
323
|
+
# Parse output for status
|
|
324
|
+
enabled = "Ready" in result.stdout or "Running" in result.stdout
|
|
325
|
+
return {"exists": True, "enabled": enabled}
|
|
326
|
+
else:
|
|
327
|
+
return {"exists": False, "enabled": False}
|
|
328
|
+
|
|
329
|
+
except Exception:
|
|
330
|
+
return {"exists": False, "enabled": False}
|