mcp-vector-search 0.4.14__py3-none-any.whl → 0.5.1__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.

Files changed (30) hide show
  1. mcp_vector_search/__init__.py +2 -2
  2. mcp_vector_search/cli/commands/index.py +73 -31
  3. mcp_vector_search/cli/commands/init.py +189 -113
  4. mcp_vector_search/cli/commands/install.py +525 -113
  5. mcp_vector_search/cli/commands/mcp.py +201 -151
  6. mcp_vector_search/cli/commands/reset.py +41 -41
  7. mcp_vector_search/cli/commands/search.py +73 -14
  8. mcp_vector_search/cli/commands/status.py +51 -17
  9. mcp_vector_search/cli/didyoumean.py +254 -246
  10. mcp_vector_search/cli/main.py +114 -43
  11. mcp_vector_search/cli/output.py +152 -0
  12. mcp_vector_search/cli/suggestions.py +246 -197
  13. mcp_vector_search/core/database.py +81 -49
  14. mcp_vector_search/core/indexer.py +10 -4
  15. mcp_vector_search/core/search.py +17 -6
  16. mcp_vector_search/mcp/__main__.py +1 -1
  17. mcp_vector_search/mcp/server.py +211 -203
  18. mcp_vector_search/parsers/__init__.py +7 -0
  19. mcp_vector_search/parsers/dart.py +605 -0
  20. mcp_vector_search/parsers/html.py +413 -0
  21. mcp_vector_search/parsers/php.py +694 -0
  22. mcp_vector_search/parsers/registry.py +21 -1
  23. mcp_vector_search/parsers/ruby.py +678 -0
  24. mcp_vector_search/parsers/text.py +32 -26
  25. mcp_vector_search/utils/gitignore.py +72 -71
  26. {mcp_vector_search-0.4.14.dist-info → mcp_vector_search-0.5.1.dist-info}/METADATA +76 -5
  27. {mcp_vector_search-0.4.14.dist-info → mcp_vector_search-0.5.1.dist-info}/RECORD +30 -26
  28. {mcp_vector_search-0.4.14.dist-info → mcp_vector_search-0.5.1.dist-info}/WHEEL +0 -0
  29. {mcp_vector_search-0.4.14.dist-info → mcp_vector_search-0.5.1.dist-info}/entry_points.txt +0 -0
  30. {mcp_vector_search-0.4.14.dist-info → mcp_vector_search-0.5.1.dist-info}/licenses/LICENSE +0 -0
@@ -2,306 +2,355 @@
2
2
 
3
3
  import json
4
4
  from pathlib import Path
5
- from typing import Dict, List, Optional, Set, Tuple
6
- from rich.console import Console
7
5
 
8
- from ..core.project import ProjectManager
9
- from ..core.exceptions import ProjectNotFoundError
6
+ from rich.console import Console
10
7
 
11
8
 
12
9
  class ContextualSuggestionProvider:
13
10
  """Provides context-aware suggestions based on project state and user workflow."""
14
-
15
- def __init__(self, project_root: Optional[Path] = None):
11
+
12
+ def __init__(self, project_root: Path | None = None):
16
13
  """Initialize the suggestion provider.
17
-
14
+
18
15
  Args:
19
16
  project_root: Root directory of the project (defaults to current directory)
20
17
  """
21
18
  self.project_root = project_root or Path.cwd()
22
19
  self.console = Console(stderr=True)
23
-
24
- def get_project_state(self) -> Dict[str, bool]:
20
+
21
+ def get_project_state(self) -> dict[str, bool]:
25
22
  """Analyze the current project state.
26
-
23
+
27
24
  Returns:
28
25
  Dictionary with boolean flags indicating project state
29
26
  """
30
27
  state = {
31
- 'is_initialized': False,
32
- 'has_index': False,
33
- 'has_config': False,
34
- 'has_recent_changes': False,
35
- 'is_git_repo': False,
36
- 'has_mcp_config': False
28
+ "is_initialized": False,
29
+ "has_index": False,
30
+ "has_config": False,
31
+ "has_recent_changes": False,
32
+ "is_git_repo": False,
33
+ "has_mcp_config": False,
37
34
  }
38
-
35
+
39
36
  try:
40
37
  # Check if project is initialized
41
- config_dir = self.project_root / '.mcp-vector-search'
42
- state['is_initialized'] = config_dir.exists()
43
-
44
- if state['is_initialized']:
38
+ config_dir = self.project_root / ".mcp-vector-search"
39
+ state["is_initialized"] = config_dir.exists()
40
+
41
+ if state["is_initialized"]:
45
42
  # Check for config
46
- config_file = config_dir / 'config.json'
47
- state['has_config'] = config_file.exists()
48
-
43
+ config_file = config_dir / "config.json"
44
+ state["has_config"] = config_file.exists()
45
+
49
46
  # Check for index
50
- index_dir = config_dir / 'chroma_db'
51
- state['has_index'] = index_dir.exists() and any(index_dir.iterdir())
52
-
47
+ index_dir = config_dir / "chroma_db"
48
+ state["has_index"] = index_dir.exists() and any(index_dir.iterdir())
49
+
53
50
  # Check if it's a git repo
54
- git_dir = self.project_root / '.git'
55
- state['is_git_repo'] = git_dir.exists()
56
-
51
+ git_dir = self.project_root / ".git"
52
+ state["is_git_repo"] = git_dir.exists()
53
+
57
54
  # Check for MCP configuration (Claude Desktop config)
58
55
  home = Path.home()
59
- claude_config = home / 'Library' / 'Application Support' / 'Claude' / 'claude_desktop_config.json'
56
+ claude_config = (
57
+ home
58
+ / "Library"
59
+ / "Application Support"
60
+ / "Claude"
61
+ / "claude_desktop_config.json"
62
+ )
60
63
  if claude_config.exists():
61
64
  try:
62
65
  with open(claude_config) as f:
63
66
  config_data = json.load(f)
64
- mcp_servers = config_data.get('mcpServers', {})
65
- state['has_mcp_config'] = 'mcp-vector-search' in mcp_servers
66
- except (json.JSONDecodeError, IOError):
67
+ mcp_servers = config_data.get("mcpServers", {})
68
+ state["has_mcp_config"] = "mcp-vector-search" in mcp_servers
69
+ except (OSError, json.JSONDecodeError):
67
70
  pass
68
-
71
+
69
72
  # TODO: Check for recent file changes (would need file system monitoring)
70
73
  # For now, we'll assume false
71
- state['has_recent_changes'] = False
72
-
74
+ state["has_recent_changes"] = False
75
+
73
76
  except Exception:
74
77
  # If we can't determine state, provide conservative defaults
75
78
  pass
76
-
79
+
77
80
  return state
78
-
79
- def get_workflow_suggestions(self, failed_command: str) -> List[Dict[str, str]]:
81
+
82
+ def get_workflow_suggestions(self, failed_command: str) -> list[dict[str, str]]:
80
83
  """Get workflow-based suggestions for a failed command.
81
-
84
+
82
85
  Args:
83
86
  failed_command: The command that failed
84
-
87
+
85
88
  Returns:
86
89
  List of suggestion dictionaries with 'command', 'reason', and 'priority'
87
90
  """
88
91
  suggestions = []
89
92
  state = self.get_project_state()
90
-
93
+
91
94
  # High priority suggestions based on project state
92
- if not state['is_initialized']:
93
- suggestions.append({
94
- 'command': 'init',
95
- 'reason': 'Project is not initialized for vector search',
96
- 'priority': 'high',
97
- 'description': 'Set up the project configuration and create necessary directories'
98
- })
99
- elif not state['has_index']:
100
- suggestions.append({
101
- 'command': 'index',
102
- 'reason': 'No search index found - create one to enable searching',
103
- 'priority': 'high',
104
- 'description': 'Build the vector index for your codebase'
105
- })
106
-
95
+ if not state["is_initialized"]:
96
+ suggestions.append(
97
+ {
98
+ "command": "init",
99
+ "reason": "Project is not initialized for vector search",
100
+ "priority": "high",
101
+ "description": "Set up the project configuration and create necessary directories",
102
+ }
103
+ )
104
+ elif not state["has_index"]:
105
+ suggestions.append(
106
+ {
107
+ "command": "index",
108
+ "reason": "No search index found - create one to enable searching",
109
+ "priority": "high",
110
+ "description": "Build the vector index for your codebase",
111
+ }
112
+ )
113
+
107
114
  # Context-specific suggestions based on the failed command
108
- if failed_command.lower() in ['search', 'find', 'query', 's', 'f']:
109
- if not state['has_index']:
110
- suggestions.append({
111
- 'command': 'index',
112
- 'reason': 'Cannot search without an index',
113
- 'priority': 'high',
114
- 'description': 'Build the search index first'
115
- })
116
- else:
117
- suggestions.extend([
115
+ if failed_command.lower() in ["search", "find", "query", "s", "f"]:
116
+ if not state["has_index"]:
117
+ suggestions.append(
118
118
  {
119
- 'command': 'search',
120
- 'reason': 'Correct command for semantic code search',
121
- 'priority': 'high',
122
- 'description': 'Search your codebase semantically'
123
- },
124
- {
125
- 'command': 'interactive',
126
- 'reason': 'Try interactive search for better experience',
127
- 'priority': 'medium',
128
- 'description': 'Start an interactive search session'
119
+ "command": "index",
120
+ "reason": "Cannot search without an index",
121
+ "priority": "high",
122
+ "description": "Build the search index first",
129
123
  }
130
- ])
131
-
132
- elif failed_command.lower() in ['index', 'build', 'scan', 'i', 'b']:
133
- suggestions.append({
134
- 'command': 'index',
135
- 'reason': 'Correct command for building search index',
136
- 'priority': 'high',
137
- 'description': 'Index your codebase for semantic search'
138
- })
139
-
140
- elif failed_command.lower() in ['status', 'info', 'stat', 'st']:
141
- suggestions.append({
142
- 'command': 'status',
143
- 'reason': 'Show project status and statistics',
144
- 'priority': 'high',
145
- 'description': 'Display current project information'
146
- })
147
-
148
- elif failed_command.lower() in ['config', 'configure', 'settings', 'c']:
149
- suggestions.append({
150
- 'command': 'config',
151
- 'reason': 'Manage project configuration',
152
- 'priority': 'high',
153
- 'description': 'View or modify project settings'
154
- })
155
-
124
+ )
125
+ else:
126
+ suggestions.extend(
127
+ [
128
+ {
129
+ "command": "search",
130
+ "reason": "Correct command for semantic code search",
131
+ "priority": "high",
132
+ "description": "Search your codebase semantically",
133
+ },
134
+ {
135
+ "command": "interactive",
136
+ "reason": "Try interactive search for better experience",
137
+ "priority": "medium",
138
+ "description": "Start an interactive search session",
139
+ },
140
+ ]
141
+ )
142
+
143
+ elif failed_command.lower() in ["index", "build", "scan", "i", "b"]:
144
+ suggestions.append(
145
+ {
146
+ "command": "index",
147
+ "reason": "Correct command for building search index",
148
+ "priority": "high",
149
+ "description": "Index your codebase for semantic search",
150
+ }
151
+ )
152
+
153
+ elif failed_command.lower() in ["status", "info", "stat", "st"]:
154
+ suggestions.append(
155
+ {
156
+ "command": "status",
157
+ "reason": "Show project status and statistics",
158
+ "priority": "high",
159
+ "description": "Display current project information",
160
+ }
161
+ )
162
+
163
+ elif failed_command.lower() in ["config", "configure", "settings", "c"]:
164
+ suggestions.append(
165
+ {
166
+ "command": "config",
167
+ "reason": "Manage project configuration",
168
+ "priority": "high",
169
+ "description": "View or modify project settings",
170
+ }
171
+ )
172
+
156
173
  # MCP-related suggestions
157
- if not state['has_mcp_config'] and failed_command.lower() in ['mcp', 'claude', 'server']:
158
- suggestions.append({
159
- 'command': 'init-mcp',
160
- 'reason': 'Set up Claude Code MCP integration',
161
- 'priority': 'medium',
162
- 'description': 'Configure MCP server for Claude Code integration'
163
- })
164
-
174
+ if not state["has_mcp_config"] and failed_command.lower() in [
175
+ "mcp",
176
+ "claude",
177
+ "server",
178
+ ]:
179
+ suggestions.append(
180
+ {
181
+ "command": "init-mcp",
182
+ "reason": "Set up Claude Code MCP integration",
183
+ "priority": "medium",
184
+ "description": "Configure MCP server for Claude Code integration",
185
+ }
186
+ )
187
+
165
188
  # Remove duplicates while preserving order
166
189
  seen = set()
167
190
  unique_suggestions = []
168
191
  for suggestion in suggestions:
169
- key = suggestion['command']
192
+ key = suggestion["command"]
170
193
  if key not in seen:
171
194
  seen.add(key)
172
195
  unique_suggestions.append(suggestion)
173
-
196
+
174
197
  return unique_suggestions
175
-
176
- def get_next_steps(self) -> List[Dict[str, str]]:
198
+
199
+ def get_next_steps(self) -> list[dict[str, str]]:
177
200
  """Get suggested next steps based on current project state.
178
-
201
+
179
202
  Returns:
180
203
  List of suggested next step dictionaries
181
204
  """
182
205
  state = self.get_project_state()
183
206
  next_steps = []
184
-
185
- if not state['is_initialized']:
186
- next_steps.append({
187
- 'command': 'init',
188
- 'description': 'Initialize the project for semantic search',
189
- 'priority': 'high'
190
- })
191
- elif not state['has_index']:
192
- next_steps.append({
193
- 'command': 'index',
194
- 'description': 'Build the search index for your codebase',
195
- 'priority': 'high'
196
- })
207
+
208
+ if not state["is_initialized"]:
209
+ next_steps.append(
210
+ {
211
+ "command": "init",
212
+ "description": "Initialize the project for semantic search",
213
+ "priority": "high",
214
+ }
215
+ )
216
+ elif not state["has_index"]:
217
+ next_steps.append(
218
+ {
219
+ "command": "index",
220
+ "description": "Build the search index for your codebase",
221
+ "priority": "high",
222
+ }
223
+ )
197
224
  else:
198
225
  # Project is ready for use
199
- next_steps.extend([
200
- {
201
- 'command': 'search "your query here"',
202
- 'description': 'Search your codebase semantically',
203
- 'priority': 'high'
204
- },
226
+ next_steps.extend(
227
+ [
228
+ {
229
+ "command": 'search "your query here"',
230
+ "description": "Search your codebase semantically",
231
+ "priority": "high",
232
+ },
233
+ {
234
+ "command": "status",
235
+ "description": "Check project statistics and index health",
236
+ "priority": "medium",
237
+ },
238
+ ]
239
+ )
240
+
241
+ if state["has_recent_changes"]:
242
+ next_steps.insert(
243
+ 0,
244
+ {
245
+ "command": "index --force",
246
+ "description": "Update the index with recent changes",
247
+ "priority": "high",
248
+ },
249
+ )
250
+
251
+ if not state["has_mcp_config"] and state["is_initialized"]:
252
+ next_steps.append(
205
253
  {
206
- 'command': 'status',
207
- 'description': 'Check project statistics and index health',
208
- 'priority': 'medium'
254
+ "command": "init-mcp",
255
+ "description": "Set up Claude Code integration",
256
+ "priority": "low",
209
257
  }
210
- ])
211
-
212
- if state['has_recent_changes']:
213
- next_steps.insert(0, {
214
- 'command': 'index --force',
215
- 'description': 'Update the index with recent changes',
216
- 'priority': 'high'
217
- })
218
-
219
- if not state['has_mcp_config'] and state['is_initialized']:
220
- next_steps.append({
221
- 'command': 'init-mcp',
222
- 'description': 'Set up Claude Code integration',
223
- 'priority': 'low'
224
- })
225
-
258
+ )
259
+
226
260
  return next_steps
227
-
228
- def show_contextual_help(self, failed_command: Optional[str] = None) -> None:
261
+
262
+ def show_contextual_help(self, failed_command: str | None = None) -> None:
229
263
  """Show contextual help and suggestions.
230
-
264
+
231
265
  Args:
232
266
  failed_command: The command that failed (if any)
233
267
  """
234
268
  if failed_command:
235
- self.console.print(f"\n[yellow]Command '{failed_command}' not recognized.[/yellow]")
236
-
269
+ self.console.print(
270
+ f"\n[yellow]Command '{failed_command}' not recognized.[/yellow]"
271
+ )
272
+
237
273
  suggestions = self.get_workflow_suggestions(failed_command)
238
274
  if suggestions:
239
- self.console.print("\n[bold]Based on your project state, you might want to try:[/bold]")
240
-
275
+ self.console.print(
276
+ "\n[bold]Based on your project state, you might want to try:[/bold]"
277
+ )
278
+
241
279
  for i, suggestion in enumerate(suggestions[:3], 1): # Show top 3
242
280
  priority_color = {
243
- 'high': 'red',
244
- 'medium': 'yellow',
245
- 'low': 'dim'
246
- }.get(suggestion['priority'], 'white')
247
-
281
+ "high": "red",
282
+ "medium": "yellow",
283
+ "low": "dim",
284
+ }.get(suggestion["priority"], "white")
285
+
248
286
  self.console.print(
249
287
  f" [{priority_color}]{i}.[/{priority_color}] "
250
288
  f"[bold cyan]mcp-vector-search {suggestion['command']}[/bold cyan]"
251
289
  )
252
290
  self.console.print(f" {suggestion['description']}")
253
- if suggestion.get('reason'):
291
+ if suggestion.get("reason"):
254
292
  self.console.print(f" [dim]({suggestion['reason']})[/dim]")
255
293
  else:
256
294
  # Show general next steps
257
295
  next_steps = self.get_next_steps()
258
296
  if next_steps:
259
297
  self.console.print("\n[bold]Suggested next steps:[/bold]")
260
-
298
+
261
299
  for i, step in enumerate(next_steps[:3], 1):
262
300
  priority_color = {
263
- 'high': 'green',
264
- 'medium': 'yellow',
265
- 'low': 'dim'
266
- }.get(step['priority'], 'white')
267
-
301
+ "high": "green",
302
+ "medium": "yellow",
303
+ "low": "dim",
304
+ }.get(step["priority"], "white")
305
+
268
306
  self.console.print(
269
307
  f" [{priority_color}]{i}.[/{priority_color}] "
270
308
  f"[bold cyan]mcp-vector-search {step['command']}[/bold cyan]"
271
309
  )
272
310
  self.console.print(f" {step['description']}")
273
-
274
- def get_command_completion_suggestions(self, partial_command: str) -> List[str]:
311
+
312
+ def get_command_completion_suggestions(self, partial_command: str) -> list[str]:
275
313
  """Get command completion suggestions for a partial command.
276
-
314
+
277
315
  Args:
278
316
  partial_command: Partial command string
279
-
317
+
280
318
  Returns:
281
319
  List of possible command completions
282
320
  """
283
321
  all_commands = [
284
- 'search', 'index', 'status', 'config', 'init', 'mcp',
285
- 'doctor', 'version', 'watch', 'auto-index', 'history',
286
- 'interactive', 'demo', 'install', 'reset', 'health'
322
+ "search",
323
+ "index",
324
+ "status",
325
+ "config",
326
+ "init",
327
+ "mcp",
328
+ "doctor",
329
+ "version",
330
+ "watch",
331
+ "auto-index",
332
+ "history",
333
+ "interactive",
334
+ "demo",
335
+ "install",
336
+ "reset",
337
+ "health",
287
338
  ]
288
-
339
+
289
340
  # Add common aliases and shortcuts
290
- all_commands.extend(['s', 'i', 'st', 'c', 'f', 'find'])
291
-
341
+ all_commands.extend(["s", "i", "st", "c", "f", "find"])
342
+
292
343
  partial_lower = partial_command.lower()
293
- matches = [
294
- cmd for cmd in all_commands
295
- if cmd.startswith(partial_lower)
296
- ]
297
-
344
+ matches = [cmd for cmd in all_commands if cmd.startswith(partial_lower)]
345
+
298
346
  return sorted(matches)
299
347
 
300
348
 
301
- def get_contextual_suggestions(project_root: Optional[Path] = None,
302
- failed_command: Optional[str] = None) -> None:
349
+ def get_contextual_suggestions(
350
+ project_root: Path | None = None, failed_command: str | None = None
351
+ ) -> None:
303
352
  """Get and display contextual suggestions.
304
-
353
+
305
354
  Args:
306
355
  project_root: Root directory of the project
307
356
  failed_command: The command that failed
@@ -310,16 +359,16 @@ def get_contextual_suggestions(project_root: Optional[Path] = None,
310
359
  provider.show_contextual_help(failed_command)
311
360
 
312
361
 
313
- def suggest_workflow_commands(project_root: Optional[Path] = None) -> List[str]:
362
+ def suggest_workflow_commands(project_root: Path | None = None) -> list[str]:
314
363
  """Get workflow command suggestions for the current project state.
315
-
364
+
316
365
  Args:
317
366
  project_root: Root directory of the project
318
-
367
+
319
368
  Returns:
320
369
  List of suggested commands in priority order
321
370
  """
322
371
  provider = ContextualSuggestionProvider(project_root)
323
372
  next_steps = provider.get_next_steps()
324
-
325
- return [step['command'] for step in next_steps]
373
+
374
+ return [step["command"] for step in next_steps]