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

@@ -3,14 +3,17 @@
3
3
  import click
4
4
  import typer
5
5
  from click_didyoumean import DYMGroup
6
- from typing import Any, Dict, Optional
6
+ from typing import Any, Dict, List, Optional, Tuple
7
+ import difflib
8
+ from pathlib import Path
9
+ import sys
7
10
 
8
11
 
9
- class DidYouMeanTyper(typer.Typer):
10
- """Enhanced Typer class with 'did you mean' functionality."""
12
+ class EnhancedDidYouMeanTyper(typer.Typer):
13
+ """Enhanced Typer class with advanced 'did you mean' functionality."""
11
14
 
12
15
  def __init__(self, *args, **kwargs):
13
- """Initialize with did-you-mean support."""
16
+ """Initialize with enhanced did-you-mean support."""
14
17
  # Extract Typer-specific kwargs
15
18
  typer_kwargs = {}
16
19
  click_kwargs = {}
@@ -35,55 +38,90 @@ class DidYouMeanTyper(typer.Typer):
35
38
 
36
39
  # Store click kwargs for later use
37
40
  self._click_kwargs = click_kwargs
41
+ self.command_aliases = {} # Store command aliases
38
42
 
39
43
  def __call__(self, *args, **kwargs):
40
- """Override call to use DYMGroup."""
44
+ """Override call to use enhanced DYMGroup."""
41
45
  # Get the underlying click group
42
46
  click_group = super().__call__(*args, **kwargs)
43
47
 
44
- # Create a new DYMGroup with the same properties
45
- dym_group = DYMGroup(
48
+ # Create enhanced DYM group with original group's properties
49
+ enhanced_group = EnhancedDidYouMeanGroup(
46
50
  name=click_group.name,
47
51
  commands=click_group.commands,
52
+ callback=click_group.callback,
53
+ params=click_group.params,
54
+ help=click_group.help,
55
+ epilog=click_group.epilog,
56
+ short_help=click_group.short_help,
57
+ add_help_option=click_group.add_help_option,
58
+ context_settings=click_group.context_settings,
59
+ invoke_without_command=click_group.invoke_without_command,
60
+ no_args_is_help=click_group.no_args_is_help,
61
+ subcommand_metavar=click_group.subcommand_metavar,
62
+ chain=click_group.chain,
63
+ result_callback=click_group.result_callback,
64
+ deprecated=click_group.deprecated,
48
65
  **self._click_kwargs
49
66
  )
50
67
 
51
- # Copy all attributes from the original group
52
- for attr in dir(click_group):
53
- if not attr.startswith('_') and attr not in ['commands', 'name']:
54
- try:
55
- setattr(dym_group, attr, getattr(click_group, attr))
56
- except (AttributeError, TypeError):
57
- # Skip attributes that can't be set
58
- pass
68
+ # Additional attributes that might be needed
69
+ if hasattr(click_group, 'options_metavar'):
70
+ enhanced_group.options_metavar = click_group.options_metavar
59
71
 
60
- return dym_group
72
+ return enhanced_group
73
+
74
+ def add_alias(self, command_name: str, alias: str) -> None:
75
+ """Add an alias for a command.
76
+
77
+ Args:
78
+ command_name: The original command name
79
+ alias: The alias to add
80
+ """
81
+ self.command_aliases[alias] = command_name
61
82
 
62
83
 
63
- class DidYouMeanGroup(DYMGroup):
64
- """Custom Click group with enhanced 'did you mean' functionality."""
84
+ class EnhancedDidYouMeanGroup(DYMGroup):
85
+ """Enhanced Click group with advanced 'did you mean' functionality."""
65
86
 
66
87
  def __init__(self, *args, **kwargs):
67
- """Initialize with better error messages."""
88
+ """Initialize with better error messages and fuzzy matching."""
68
89
  super().__init__(*args, **kwargs)
90
+ self.max_suggestions = 3 # Maximum number of suggestions to show
69
91
 
70
92
  def resolve_command(self, ctx: click.Context, args: list) -> tuple:
71
- """Resolve command with enhanced error handling."""
93
+ """Resolve command with enhanced error handling and suggestions."""
72
94
  try:
73
95
  return super().resolve_command(ctx, args)
74
96
  except click.UsageError as e:
75
- # Enhance the error message with available commands
76
- if "No such command" in str(e):
77
- available_commands = list(self.commands.keys())
78
- if available_commands:
79
- commands_list = ", ".join(f"'{cmd}'" for cmd in sorted(available_commands))
80
- enhanced_msg = f"{str(e)}\n\nAvailable commands: {commands_list}"
81
- raise click.UsageError(enhanced_msg, ctx=ctx)
97
+ # Enhanced error handling with better suggestions
98
+ if "No such command" in str(e) and args:
99
+ command_name = args[0]
100
+
101
+ # Use our enhanced suggestion system
102
+ add_common_suggestions(ctx, command_name)
103
+
104
+ # Re-raise with original message (suggestions already printed)
105
+ raise click.UsageError(str(e), ctx=ctx)
82
106
  raise
107
+
108
+ def get_command(self, ctx: click.Context, cmd_name: str):
109
+ """Get command with support for aliases and shortcuts."""
110
+ # First try exact match
111
+ rv = super().get_command(ctx, cmd_name)
112
+ if rv is not None:
113
+ return rv
114
+
115
+ # Try common typo mappings
116
+ suggestion = COMMON_TYPOS.get(cmd_name.lower())
117
+ if suggestion and ' ' not in suggestion: # Only single commands, not flags
118
+ return super().get_command(ctx, suggestion)
119
+
120
+ return None
83
121
 
84
122
 
85
123
  def create_enhanced_typer(**kwargs) -> typer.Typer:
86
- """Create a Typer instance with 'did you mean' functionality."""
124
+ """Create a Typer instance with enhanced 'did you mean' functionality."""
87
125
  # Set default values for better UX
88
126
  defaults = {
89
127
  'no_args_is_help': True,
@@ -95,24 +133,24 @@ def create_enhanced_typer(**kwargs) -> typer.Typer:
95
133
  final_kwargs = {**defaults, **kwargs}
96
134
 
97
135
  # Create the enhanced Typer
98
- app = DidYouMeanTyper(**final_kwargs)
136
+ app = EnhancedDidYouMeanTyper(**final_kwargs)
99
137
 
100
138
  return app
101
139
 
102
140
 
103
141
  def enhance_existing_typer(app: typer.Typer) -> typer.Typer:
104
- """Enhance an existing Typer app with 'did you mean' functionality."""
142
+ """Enhance an existing Typer app with advanced 'did you mean' functionality."""
105
143
  # This is a bit tricky since we need to modify the underlying Click group
106
144
  # We'll create a wrapper that intercepts the click group creation
107
145
 
108
146
  original_call = app.__call__
109
147
 
110
148
  def enhanced_call(*args, **kwargs):
111
- """Enhanced call that uses DYMGroup."""
149
+ """Enhanced call that uses EnhancedDidYouMeanGroup."""
112
150
  click_group = original_call(*args, **kwargs)
113
151
 
114
152
  # Create enhanced group
115
- enhanced_group = DidYouMeanGroup(
153
+ enhanced_group = EnhancedDidYouMeanGroup(
116
154
  name=click_group.name,
117
155
  commands=click_group.commands,
118
156
  callback=click_group.callback,
@@ -137,48 +175,309 @@ def enhance_existing_typer(app: typer.Typer) -> typer.Typer:
137
175
  return app
138
176
 
139
177
 
140
- def add_common_suggestions(ctx: click.Context, command_name: str) -> None:
141
- """Add common command suggestions to error messages."""
142
- common_typos = {
143
- 'serach': 'search',
144
- 'seach': 'search',
145
- 'searh': 'search',
146
- 'find': 'search',
147
- 'indx': 'index',
148
- 'idx': 'index',
149
- 'reindex': 'index --force',
150
- 'stat': 'status',
151
- 'stats': 'status',
152
- 'info': 'status',
153
- 'conf': 'config',
154
- 'cfg': 'config',
155
- 'setting': 'config',
156
- 'settings': 'config',
157
- 'init': 'init',
158
- 'initialize': 'init',
159
- 'setup': 'init',
160
- 'start': 'init',
161
- 'watch': 'watch',
162
- 'monitor': 'watch',
163
- 'auto': 'auto-index',
164
- 'automatic': 'auto-index',
165
- 'mcp': 'mcp',
166
- 'claude': 'mcp',
167
- 'server': 'mcp',
168
- 'install': 'install',
169
- 'setup': 'install',
170
- 'demo': 'demo',
171
- 'example': 'demo',
172
- 'test': 'mcp test',
173
- 'check': 'status',
174
- 'doctor': 'doctor',
175
- 'health': 'doctor',
176
- 'version': 'version',
177
- 'ver': 'version',
178
- 'help': '--help',
179
- 'h': '--help',
178
+ # Enhanced typo mapping with comprehensive variations
179
+ COMMON_TYPOS = {
180
+ # Search command variations
181
+ 'serach': 'search',
182
+ 'seach': 'search',
183
+ 'searh': 'search',
184
+ 'sarch': 'search',
185
+ 'serch': 'search',
186
+ 'searhc': 'search',
187
+ 'find': 'search',
188
+ 'query': 'search',
189
+ 'lookup': 'search',
190
+ 'grep': 'search',
191
+ 's': 'search', # Single letter shortcut
192
+ 'f': 'search', # Alternative shortcut for find
193
+
194
+ # Index command variations
195
+ 'indx': 'index',
196
+ 'idx': 'index',
197
+ 'indexx': 'index',
198
+ 'indez': 'index',
199
+ 'inedx': 'index',
200
+ 'reindex': 'index --force',
201
+ 'rebuild': 'index --force',
202
+ 'refresh': 'index --force',
203
+ 'scan': 'index',
204
+ 'build': 'index',
205
+ 'i': 'index', # Single letter shortcut
206
+ 'b': 'index', # Alternative shortcut for build
207
+
208
+ # Status command variations
209
+ 'stat': 'status',
210
+ 'stats': 'status',
211
+ 'info': 'status',
212
+ 'information': 'status',
213
+ 'details': 'status',
214
+ 'summary': 'status',
215
+ 'overview': 'status',
216
+ 'st': 'status', # Common abbreviation
217
+ 'status': 'status',
218
+
219
+ # Config command variations
220
+ 'conf': 'config',
221
+ 'cfg': 'config',
222
+ 'configure': 'config',
223
+ 'configuration': 'config',
224
+ 'setting': 'config',
225
+ 'settings': 'config',
226
+ 'preferences': 'config',
227
+ 'prefs': 'config',
228
+ 'c': 'config', # Single letter shortcut
229
+
230
+ # Init command variations
231
+ 'initialize': 'init',
232
+ 'setup': 'init',
233
+ 'start': 'init',
234
+ 'create': 'init',
235
+ 'new': 'init',
236
+ 'begin': 'init',
237
+ 'initalize': 'init', # Common misspelling
238
+ 'initalise': 'init',
239
+ 'initialise': 'init',
240
+
241
+ # Watch command variations
242
+ 'monitor': 'watch',
243
+ 'observe': 'watch',
244
+ 'track': 'watch',
245
+ 'listen': 'watch',
246
+ 'follow': 'watch',
247
+ 'w': 'watch', # Single letter shortcut
248
+
249
+ # Auto-index command variations
250
+ 'auto': 'auto-index',
251
+ 'automatic': 'auto-index',
252
+ 'autoindex': 'auto-index',
253
+ 'auto_index': 'auto-index',
254
+ 'ai': 'auto-index', # Abbreviation
255
+
256
+ # MCP command variations
257
+ 'claude': 'mcp',
258
+ 'server': 'mcp',
259
+ 'protocol': 'mcp',
260
+ 'model-context': 'mcp',
261
+ 'context': 'mcp',
262
+ 'm': 'mcp', # Single letter shortcut
263
+
264
+ # Install command variations
265
+ 'setup': 'install',
266
+ 'deploy': 'install',
267
+ 'add': 'install',
268
+ 'instal': 'install', # Common typo
269
+ 'install': 'install',
270
+
271
+ # Demo command variations
272
+ 'example': 'demo',
273
+ 'sample': 'demo',
274
+ 'test': 'demo',
275
+ 'try': 'demo',
276
+ 'd': 'demo', # Single letter shortcut
277
+
278
+ # Health/Doctor command variations
279
+ 'check': 'doctor',
280
+ 'health': 'doctor',
281
+ 'diagnose': 'doctor',
282
+ 'verify': 'doctor',
283
+ 'validate': 'doctor',
284
+ 'repair': 'doctor',
285
+ 'fix': 'doctor',
286
+ 'dr': 'doctor', # Common abbreviation
287
+
288
+ # Version command variations
289
+ 'ver': 'version',
290
+ 'v': 'version',
291
+ '--version': 'version',
292
+ '-v': 'version',
293
+
294
+ # Help command variations
295
+ 'help': '--help',
296
+ 'h': '--help',
297
+ '--help': '--help',
298
+ '-h': '--help',
299
+ '?': '--help',
300
+
301
+ # History command variations
302
+ 'hist': 'history',
303
+ 'log': 'history',
304
+ 'logs': 'history',
305
+ 'recent': 'history',
306
+
307
+ # Reset command variations
308
+ 'clear': 'reset',
309
+ 'clean': 'reset',
310
+ 'purge': 'reset',
311
+ 'wipe': 'reset',
312
+ 'remove': 'reset',
313
+ 'delete': 'reset',
314
+
315
+ # Interactive command variations
316
+ 'interact': 'interactive',
317
+ 'session': 'interactive',
318
+ 'repl': 'interactive',
319
+ 'console': 'interactive',
320
+ 'ui': 'interactive',
321
+ }
322
+
323
+ # Command descriptions and examples for better error messages
324
+ COMMAND_INFO = {
325
+ 'search': {
326
+ 'description': 'Search for code patterns semantically',
327
+ 'examples': [
328
+ 'mcp-vector-search search "authentication function"',
329
+ 'mcp-vector-search search "error handling" --limit 5',
330
+ 'mcp-vector-search find "database connection"'
331
+ ],
332
+ 'related': ['search-similar', 'search-context', 'find', 'interactive']
333
+ },
334
+ 'index': {
335
+ 'description': 'Index codebase for semantic search',
336
+ 'examples': [
337
+ 'mcp-vector-search index',
338
+ 'mcp-vector-search index --force',
339
+ 'mcp-vector-search index --include "*.py,*.js"'
340
+ ],
341
+ 'related': ['auto-index', 'watch', 'reset']
342
+ },
343
+ 'status': {
344
+ 'description': 'Show project status and statistics',
345
+ 'examples': [
346
+ 'mcp-vector-search status',
347
+ 'mcp-vector-search status --verbose'
348
+ ],
349
+ 'related': ['doctor', 'history', 'version']
350
+ },
351
+ 'config': {
352
+ 'description': 'Manage project configuration',
353
+ 'examples': [
354
+ 'mcp-vector-search config show',
355
+ 'mcp-vector-search config set model all-MiniLM-L6-v2'
356
+ ],
357
+ 'related': ['init', 'status']
358
+ },
359
+ 'init': {
360
+ 'description': 'Initialize project for semantic search',
361
+ 'examples': [
362
+ 'mcp-vector-search init',
363
+ 'mcp-vector-search init --model sentence-transformers/all-MiniLM-L6-v2'
364
+ ],
365
+ 'related': ['config', 'install', 'index']
366
+ },
367
+ 'mcp': {
368
+ 'description': 'Manage Claude Code MCP integration',
369
+ 'examples': [
370
+ 'mcp-vector-search mcp',
371
+ 'mcp-vector-search mcp test'
372
+ ],
373
+ 'related': ['init-mcp', 'install']
374
+ },
375
+ 'doctor': {
376
+ 'description': 'Check system dependencies and configuration',
377
+ 'examples': [
378
+ 'mcp-vector-search doctor',
379
+ ],
380
+ 'related': ['status', 'health']
381
+ },
382
+ 'version': {
383
+ 'description': 'Show version information',
384
+ 'examples': [
385
+ 'mcp-vector-search version',
386
+ 'mcp-vector-search --version'
387
+ ],
388
+ 'related': ['status', 'doctor']
180
389
  }
390
+ }
391
+
392
+
393
+ def get_fuzzy_matches(command: str, available_commands: List[str], cutoff: float = 0.6) -> List[Tuple[str, float]]:
394
+ """Get fuzzy matches for a command using difflib.
395
+
396
+ Args:
397
+ command: The command to match
398
+ available_commands: List of available commands
399
+ cutoff: Minimum similarity ratio (0.0 to 1.0)
400
+
401
+ Returns:
402
+ List of tuples (command, similarity_ratio) sorted by similarity
403
+ """
404
+ matches = []
405
+ for cmd in available_commands:
406
+ ratio = difflib.SequenceMatcher(None, command.lower(), cmd.lower()).ratio()
407
+ if ratio >= cutoff:
408
+ matches.append((cmd, ratio))
409
+
410
+ # Sort by similarity ratio (highest first)
411
+ return sorted(matches, key=lambda x: x[1], reverse=True)
412
+
413
+
414
+ def format_command_suggestion(command: str, show_examples: bool = True) -> str:
415
+ """Format a command suggestion with description and examples.
416
+
417
+ Args:
418
+ command: The command to format
419
+ show_examples: Whether to include usage examples
420
+
421
+ Returns:
422
+ Formatted suggestion string
423
+ """
424
+ if command in COMMAND_INFO:
425
+ info = COMMAND_INFO[command]
426
+ suggestion = f" [bold cyan]{command}[/bold cyan] - {info['description']}"
427
+
428
+ if show_examples and info['examples']:
429
+ suggestion += f"\n Example: [dim]{info['examples'][0]}[/dim]"
430
+
431
+ return suggestion
432
+ else:
433
+ return f" [bold cyan]{command}[/bold cyan]"
434
+
435
+
436
+ def add_common_suggestions(ctx: click.Context, command_name: str) -> None:
437
+ """Add enhanced command suggestions to error messages.
438
+
439
+ Args:
440
+ ctx: Click context
441
+ command_name: The invalid command name that was entered
442
+ """
443
+ from rich.console import Console
444
+ console = Console(stderr=True)
445
+
446
+ # First, check for exact typo matches
447
+ direct_suggestion = COMMON_TYPOS.get(command_name.lower())
448
+ if direct_suggestion:
449
+ console.print(f"\n[yellow]Did you mean:[/yellow]")
450
+ console.print(format_command_suggestion(direct_suggestion.split()[0]))
451
+
452
+ if '--' not in direct_suggestion: # Don't show examples for flags
453
+ console.print(f"\n[dim]Try: [bold]mcp-vector-search {direct_suggestion}[/bold][/dim]")
454
+ return
455
+
456
+ # Get available commands from the context
457
+ available_commands = []
458
+ if hasattr(ctx, 'command') and hasattr(ctx.command, 'commands'):
459
+ available_commands = list(ctx.command.commands.keys())
460
+
461
+ if not available_commands:
462
+ # Fallback to common commands
463
+ available_commands = ['search', 'index', 'status', 'config', 'init', 'mcp', 'doctor', 'version']
464
+
465
+ # Get fuzzy matches
466
+ fuzzy_matches = get_fuzzy_matches(command_name, available_commands, cutoff=0.4)
467
+
468
+ if fuzzy_matches:
469
+ console.print(f"\n[yellow]Did you mean one of these?[/yellow]")
470
+
471
+ # Show up to 3 best matches
472
+ for cmd, ratio in fuzzy_matches[:3]:
473
+ console.print(format_command_suggestion(cmd, show_examples=False))
474
+
475
+ # Show example for the best match
476
+ if fuzzy_matches:
477
+ best_match = fuzzy_matches[0][0]
478
+ if best_match in COMMAND_INFO and COMMAND_INFO[best_match]['examples']:
479
+ console.print(f"\n[dim]Example: [bold]{COMMAND_INFO[best_match]['examples'][0]}[/bold][/dim]")
181
480
 
182
- if command_name.lower() in common_typos:
183
- suggestion = common_typos[command_name.lower()]
184
- click.echo(f"\nDid you mean: mcp-vector-search {suggestion}?", err=True)
481
+ # Show related commands for context
482
+ console.print(f"\n[dim]Available commands: {', '.join(sorted(available_commands))}[/dim]")
483
+ console.print(f"[dim]Use [bold]mcp-vector-search --help[/bold] for more information[/dim]")