mcp-vector-search 0.0.3__py3-none-any.whl → 0.4.11__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 (49) hide show
  1. mcp_vector_search/__init__.py +3 -2
  2. mcp_vector_search/cli/commands/auto_index.py +397 -0
  3. mcp_vector_search/cli/commands/config.py +88 -40
  4. mcp_vector_search/cli/commands/index.py +198 -52
  5. mcp_vector_search/cli/commands/init.py +472 -58
  6. mcp_vector_search/cli/commands/install.py +284 -0
  7. mcp_vector_search/cli/commands/mcp.py +495 -0
  8. mcp_vector_search/cli/commands/search.py +241 -87
  9. mcp_vector_search/cli/commands/status.py +184 -58
  10. mcp_vector_search/cli/commands/watch.py +34 -35
  11. mcp_vector_search/cli/didyoumean.py +184 -0
  12. mcp_vector_search/cli/export.py +320 -0
  13. mcp_vector_search/cli/history.py +292 -0
  14. mcp_vector_search/cli/interactive.py +342 -0
  15. mcp_vector_search/cli/main.py +163 -26
  16. mcp_vector_search/cli/output.py +63 -45
  17. mcp_vector_search/config/defaults.py +50 -36
  18. mcp_vector_search/config/settings.py +49 -35
  19. mcp_vector_search/core/auto_indexer.py +298 -0
  20. mcp_vector_search/core/connection_pool.py +322 -0
  21. mcp_vector_search/core/database.py +335 -25
  22. mcp_vector_search/core/embeddings.py +73 -29
  23. mcp_vector_search/core/exceptions.py +19 -2
  24. mcp_vector_search/core/factory.py +310 -0
  25. mcp_vector_search/core/git_hooks.py +345 -0
  26. mcp_vector_search/core/indexer.py +237 -73
  27. mcp_vector_search/core/models.py +21 -19
  28. mcp_vector_search/core/project.py +73 -58
  29. mcp_vector_search/core/scheduler.py +330 -0
  30. mcp_vector_search/core/search.py +574 -86
  31. mcp_vector_search/core/watcher.py +48 -46
  32. mcp_vector_search/mcp/__init__.py +4 -0
  33. mcp_vector_search/mcp/__main__.py +25 -0
  34. mcp_vector_search/mcp/server.py +701 -0
  35. mcp_vector_search/parsers/base.py +30 -31
  36. mcp_vector_search/parsers/javascript.py +74 -48
  37. mcp_vector_search/parsers/python.py +57 -49
  38. mcp_vector_search/parsers/registry.py +47 -32
  39. mcp_vector_search/parsers/text.py +179 -0
  40. mcp_vector_search/utils/__init__.py +40 -0
  41. mcp_vector_search/utils/gitignore.py +229 -0
  42. mcp_vector_search/utils/timing.py +334 -0
  43. mcp_vector_search/utils/version.py +47 -0
  44. {mcp_vector_search-0.0.3.dist-info → mcp_vector_search-0.4.11.dist-info}/METADATA +173 -7
  45. mcp_vector_search-0.4.11.dist-info/RECORD +54 -0
  46. mcp_vector_search-0.0.3.dist-info/RECORD +0 -35
  47. {mcp_vector_search-0.0.3.dist-info → mcp_vector_search-0.4.11.dist-info}/WHEEL +0 -0
  48. {mcp_vector_search-0.0.3.dist-info → mcp_vector_search-0.4.11.dist-info}/entry_points.txt +0 -0
  49. {mcp_vector_search-0.0.3.dist-info → mcp_vector_search-0.4.11.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +1,10 @@
1
1
  """Status command for MCP Vector Search CLI."""
2
2
 
3
3
  import asyncio
4
+ import json
5
+ import subprocess
4
6
  from pathlib import Path
5
- from typing import Dict, Any
7
+ from typing import Any
6
8
 
7
9
  import typer
8
10
  from loguru import logger
@@ -14,13 +16,10 @@ from ...core.indexer import SemanticIndexer
14
16
  from ...core.project import ProjectManager
15
17
  from ..output import (
16
18
  console,
17
- print_config,
18
19
  print_dependency_status,
19
20
  print_error,
20
- print_index_stats,
21
21
  print_info,
22
22
  print_json,
23
- print_project_info,
24
23
  )
25
24
 
26
25
  # Create status subcommand app
@@ -30,6 +29,16 @@ status_app = typer.Typer(help="Show project status and statistics")
30
29
  @status_app.command()
31
30
  def main(
32
31
  ctx: typer.Context,
32
+ project_root: Path | None = typer.Option(
33
+ None,
34
+ "--project-root",
35
+ "-p",
36
+ help="Project root directory (auto-detected if not specified)",
37
+ exists=True,
38
+ file_okay=False,
39
+ dir_okay=True,
40
+ readable=True,
41
+ ),
33
42
  verbose: bool = typer.Option(
34
43
  False,
35
44
  "--verbose",
@@ -41,6 +50,11 @@ def main(
41
50
  "--health-check",
42
51
  help="Perform health check of all components",
43
52
  ),
53
+ mcp: bool = typer.Option(
54
+ False,
55
+ "--mcp",
56
+ help="Check Claude Code MCP integration status",
57
+ ),
44
58
  json_output: bool = typer.Option(
45
59
  False,
46
60
  "--json",
@@ -48,25 +62,31 @@ def main(
48
62
  ),
49
63
  ) -> None:
50
64
  """Show project status and indexing statistics.
51
-
65
+
52
66
  This command displays comprehensive information about your MCP Vector Search
53
67
  project including configuration, indexing status, and system health.
54
-
68
+
55
69
  Examples:
56
70
  mcp-vector-search status
57
71
  mcp-vector-search status --verbose
58
72
  mcp-vector-search status --health-check --json
73
+ mcp-vector-search status --mcp
59
74
  """
60
75
  try:
61
- project_root = ctx.obj.get("project_root") or Path.cwd()
62
-
63
- asyncio.run(show_status(
64
- project_root=project_root,
65
- verbose=verbose,
66
- health_check=health_check,
67
- json_output=json_output,
68
- ))
69
-
76
+ # Use provided project_root or current working directory
77
+ if project_root is None:
78
+ project_root = Path.cwd()
79
+
80
+ asyncio.run(
81
+ show_status(
82
+ project_root=project_root,
83
+ verbose=verbose,
84
+ health_check=health_check,
85
+ mcp=mcp,
86
+ json_output=json_output,
87
+ )
88
+ )
89
+
70
90
  except Exception as e:
71
91
  logger.error(f"Status check failed: {e}")
72
92
  print_error(f"Status check failed: {e}")
@@ -77,49 +97,50 @@ async def show_status(
77
97
  project_root: Path,
78
98
  verbose: bool = False,
79
99
  health_check: bool = False,
100
+ mcp: bool = False,
80
101
  json_output: bool = False,
81
102
  ) -> None:
82
103
  """Show comprehensive project status."""
83
104
  status_data = {}
84
-
105
+
85
106
  try:
86
- # Check if project is initialized
107
+ # Check if project is initialized - use the specified project root
87
108
  project_manager = ProjectManager(project_root)
88
-
109
+
89
110
  if not project_manager.is_initialized():
90
111
  if json_output:
91
112
  status_data = {
92
113
  "initialized": False,
93
114
  "project_root": str(project_root),
94
- "error": "Project not initialized"
115
+ "error": "Project not initialized",
95
116
  }
96
117
  print_json(status_data)
97
118
  else:
98
119
  print_error(f"Project not initialized at {project_root}")
99
120
  print_info("Run 'mcp-vector-search init' to initialize the project")
100
121
  return
101
-
122
+
102
123
  # Get project information
103
124
  project_info = project_manager.get_project_info()
104
125
  config = project_manager.load_config()
105
-
126
+
106
127
  # Get indexing statistics
107
128
  embedding_function, _ = create_embedding_function(config.embedding_model)
108
129
  database = ChromaVectorDatabase(
109
130
  persist_directory=config.index_path,
110
131
  embedding_function=embedding_function,
111
132
  )
112
-
133
+
113
134
  indexer = SemanticIndexer(
114
135
  database=database,
115
136
  project_root=project_root,
116
137
  file_extensions=config.file_extensions,
117
138
  )
118
-
139
+
119
140
  async with database:
120
141
  index_stats = await indexer.get_indexing_stats()
121
142
  db_stats = await database.get_stats()
122
-
143
+
123
144
  # Compile status data
124
145
  status_data = {
125
146
  "project": {
@@ -146,12 +167,17 @@ async def show_status(
146
167
  "last_updated": db_stats.last_updated,
147
168
  },
148
169
  }
149
-
170
+
150
171
  # Add health check if requested
151
172
  if health_check:
152
173
  health_status = await perform_health_check(project_root, config)
153
174
  status_data["health"] = health_status
154
-
175
+
176
+ # Add MCP integration check if requested
177
+ if mcp:
178
+ mcp_status = await check_mcp_integration(project_root)
179
+ status_data["mcp"] = mcp_status
180
+
155
181
  # Add verbose information
156
182
  if verbose:
157
183
  status_data["verbose"] = {
@@ -160,13 +186,13 @@ async def show_status(
160
186
  "ignore_patterns": list(indexer.get_ignore_patterns()),
161
187
  "parser_info": index_stats.get("parser_info", {}),
162
188
  }
163
-
189
+
164
190
  # Output results
165
191
  if json_output:
166
192
  print_json(status_data)
167
193
  else:
168
- _display_status(status_data, verbose)
169
-
194
+ _display_status(status_data, verbose, mcp)
195
+
170
196
  except ProjectNotFoundError:
171
197
  if json_output:
172
198
  print_json({"initialized": False, "error": "Project not initialized"})
@@ -181,45 +207,51 @@ async def show_status(
181
207
  raise
182
208
 
183
209
 
184
- def _display_status(status_data: Dict[str, Any], verbose: bool) -> None:
210
+ def _display_status(status_data: dict[str, Any], verbose: bool, mcp: bool = False) -> None:
185
211
  """Display status in human-readable format."""
186
212
  project_data = status_data["project"]
187
213
  config_data = status_data["configuration"]
188
214
  index_data = status_data["index"]
189
-
215
+
190
216
  # Project information
191
217
  console.print("[bold blue]Project Information[/bold blue]")
192
218
  console.print(f" Name: {project_data['name']}")
193
219
  console.print(f" Root: {project_data['root_path']}")
194
- console.print(f" Languages: {', '.join(project_data['languages']) if project_data['languages'] else 'None detected'}")
220
+ console.print(
221
+ f" Languages: {', '.join(project_data['languages']) if project_data['languages'] else 'None detected'}"
222
+ )
195
223
  console.print(f" Indexable Files: {project_data['file_count']}")
196
224
  console.print()
197
-
225
+
198
226
  # Configuration
199
227
  console.print("[bold blue]Configuration[/bold blue]")
200
228
  console.print(f" Embedding Model: {config_data['embedding_model']}")
201
229
  console.print(f" Similarity Threshold: {config_data['similarity_threshold']}")
202
230
  console.print(f" File Extensions: {', '.join(config_data['file_extensions'])}")
203
- console.print(f" Cache Embeddings: {'✓' if config_data['cache_embeddings'] else '✗'}")
231
+ console.print(
232
+ f" Cache Embeddings: {'✓' if config_data['cache_embeddings'] else '✗'}"
233
+ )
204
234
  console.print()
205
-
235
+
206
236
  # Index statistics
207
237
  console.print("[bold blue]Index Statistics[/bold blue]")
208
- console.print(f" Indexed Files: {index_data['indexed_files']}/{index_data['total_files']}")
238
+ console.print(
239
+ f" Indexed Files: {index_data['indexed_files']}/{index_data['total_files']}"
240
+ )
209
241
  console.print(f" Total Chunks: {index_data['total_chunks']}")
210
242
  console.print(f" Index Size: {index_data['index_size_mb']:.2f} MB")
211
-
212
- if index_data['languages']:
243
+
244
+ if index_data["languages"]:
213
245
  console.print(" Language Distribution:")
214
- for lang, count in index_data['languages'].items():
246
+ for lang, count in index_data["languages"].items():
215
247
  console.print(f" {lang}: {count} chunks")
216
248
  console.print()
217
-
249
+
218
250
  # Health check results
219
251
  if "health" in status_data:
220
252
  health_data = status_data["health"]
221
253
  console.print("[bold blue]Health Check[/bold blue]")
222
-
254
+
223
255
  overall_health = health_data.get("overall", "unknown")
224
256
  if overall_health == "healthy":
225
257
  console.print("[green]✓ System is healthy[/green]")
@@ -227,7 +259,7 @@ def _display_status(status_data: Dict[str, Any], verbose: bool) -> None:
227
259
  console.print("[yellow]⚠ System has warnings[/yellow]")
228
260
  else:
229
261
  console.print("[red]✗ System has issues[/red]")
230
-
262
+
231
263
  for component, status in health_data.get("components", {}).items():
232
264
  if status == "ok":
233
265
  console.print(f" [green]✓[/green] {component}")
@@ -236,42 +268,69 @@ def _display_status(status_data: Dict[str, Any], verbose: bool) -> None:
236
268
  else:
237
269
  console.print(f" [red]✗[/red] {component}")
238
270
  console.print()
239
-
271
+
272
+ # MCP integration status
273
+ if "mcp" in status_data:
274
+ mcp_data = status_data["mcp"]
275
+ console.print("[bold blue]MCP Integration[/bold blue]")
276
+
277
+ if mcp_data.get("claude_available"):
278
+ console.print("[green]✓[/green] Claude Code: Available")
279
+ else:
280
+ console.print("[red]✗[/red] Claude Code: Not available")
281
+
282
+ server_status = mcp_data.get("server_status", "unknown")
283
+ server_name = mcp_data.get("server_name", "mcp-vector-search")
284
+
285
+ if server_status == "installed":
286
+ console.print(f"[green]✓[/green] MCP Server '{server_name}': Installed")
287
+ elif server_status == "not_installed":
288
+ console.print(f"[red]✗[/red] MCP Server '{server_name}': Not installed")
289
+ else:
290
+ console.print(f"[yellow]⚠[/yellow] MCP Server '{server_name}': {server_status}")
291
+
292
+ if mcp_data.get("project_config"):
293
+ console.print("[green]✓[/green] Project Configuration: Found")
294
+ else:
295
+ console.print("[red]✗[/red] Project Configuration: Missing")
296
+
297
+ console.print()
298
+
240
299
  # Verbose information
241
300
  if verbose and "verbose" in status_data:
242
301
  verbose_data = status_data["verbose"]
243
302
  console.print("[bold blue]Detailed Information[/bold blue]")
244
303
  console.print(f" Config Path: {verbose_data['config_path']}")
245
304
  console.print(f" Index Path: {verbose_data['index_path']}")
246
- console.print(f" Ignore Patterns: {', '.join(verbose_data['ignore_patterns'])}")
305
+ console.print(
306
+ f" Ignore Patterns: {', '.join(verbose_data['ignore_patterns'])}"
307
+ )
247
308
 
248
309
 
249
- async def perform_health_check(project_root: Path, config) -> Dict[str, Any]:
310
+ async def perform_health_check(project_root: Path, config) -> dict[str, Any]:
250
311
  """Perform comprehensive health check."""
251
312
  health_status = {
252
313
  "overall": "healthy",
253
314
  "components": {},
254
315
  "issues": [],
255
316
  }
256
-
317
+
257
318
  try:
258
319
  # Check dependencies
259
320
  deps_ok = check_dependencies()
260
321
  health_status["components"]["dependencies"] = "ok" if deps_ok else "error"
261
322
  if not deps_ok:
262
323
  health_status["issues"].append("Missing dependencies")
263
-
324
+
264
325
  # Check configuration
265
- config_ok = True
266
326
  try:
267
327
  # Validate embedding model
268
328
  embedding_function, _ = create_embedding_function(config.embedding_model)
269
329
  health_status["components"]["embedding_model"] = "ok"
270
330
  except Exception as e:
271
- config_ok = False
272
331
  health_status["components"]["embedding_model"] = "error"
273
332
  health_status["issues"].append(f"Embedding model error: {e}")
274
-
333
+
275
334
  # Check database
276
335
  try:
277
336
  database = ChromaVectorDatabase(
@@ -284,7 +343,7 @@ async def perform_health_check(project_root: Path, config) -> Dict[str, Any]:
284
343
  except Exception as e:
285
344
  health_status["components"]["database"] = "error"
286
345
  health_status["issues"].append(f"Database error: {e}")
287
-
346
+
288
347
  # Check file system permissions
289
348
  try:
290
349
  config.index_path.mkdir(parents=True, exist_ok=True)
@@ -295,20 +354,87 @@ async def perform_health_check(project_root: Path, config) -> Dict[str, Any]:
295
354
  except Exception as e:
296
355
  health_status["components"]["file_permissions"] = "error"
297
356
  health_status["issues"].append(f"File permission error: {e}")
298
-
357
+
299
358
  # Determine overall health
300
359
  if any(status == "error" for status in health_status["components"].values()):
301
360
  health_status["overall"] = "error"
302
- elif any(status == "warning" for status in health_status["components"].values()):
361
+ elif any(
362
+ status == "warning" for status in health_status["components"].values()
363
+ ):
303
364
  health_status["overall"] = "warning"
304
-
365
+
305
366
  except Exception as e:
306
367
  health_status["overall"] = "error"
307
368
  health_status["issues"].append(f"Health check failed: {e}")
308
-
369
+
309
370
  return health_status
310
371
 
311
372
 
373
+ async def check_mcp_integration(project_root: Path, server_name: str = "mcp-vector-search") -> dict[str, Any]:
374
+ """Check MCP integration status."""
375
+ mcp_status = {
376
+ "claude_available": False,
377
+ "server_status": "unknown",
378
+ "server_name": server_name,
379
+ "project_config": False,
380
+ "issues": [],
381
+ }
382
+
383
+ try:
384
+ # Import MCP functions from the mcp command module
385
+ from .mcp import check_claude_code_available, get_claude_command
386
+
387
+ # Check if Claude Code is available
388
+ mcp_status["claude_available"] = check_claude_code_available()
389
+
390
+ if mcp_status["claude_available"]:
391
+ claude_cmd = get_claude_command()
392
+
393
+ # Check if MCP server is installed
394
+ try:
395
+ result = subprocess.run(
396
+ [claude_cmd, "mcp", "get", server_name],
397
+ capture_output=True,
398
+ text=True,
399
+ timeout=10
400
+ )
401
+
402
+ if result.returncode == 0:
403
+ mcp_status["server_status"] = "installed"
404
+ else:
405
+ mcp_status["server_status"] = "not_installed"
406
+ mcp_status["issues"].append(f"MCP server '{server_name}' not found in Claude Code")
407
+
408
+ except subprocess.TimeoutExpired:
409
+ mcp_status["server_status"] = "timeout"
410
+ mcp_status["issues"].append("Timeout checking MCP server status")
411
+ except Exception as e:
412
+ mcp_status["server_status"] = "error"
413
+ mcp_status["issues"].append(f"Error checking MCP server: {e}")
414
+ else:
415
+ mcp_status["issues"].append("Claude Code not available")
416
+
417
+ # Check for project-level .claude.json configuration
418
+ claude_json_path = project_root / ".claude.json"
419
+ if claude_json_path.exists():
420
+ try:
421
+ with open(claude_json_path, 'r') as f:
422
+ config = json.load(f)
423
+ if config.get("mcpServers", {}).get(server_name):
424
+ mcp_status["project_config"] = True
425
+ else:
426
+ mcp_status["issues"].append(f"MCP server '{server_name}' not found in project .claude.json")
427
+ except Exception as e:
428
+ mcp_status["issues"].append(f"Error reading project .claude.json: {e}")
429
+ else:
430
+ mcp_status["issues"].append("Project .claude.json not found")
431
+
432
+ except Exception as e:
433
+ mcp_status["issues"].append(f"MCP integration check failed: {e}")
434
+
435
+ return mcp_status
436
+
437
+
312
438
  def check_dependencies() -> bool:
313
439
  """Check if all required dependencies are available."""
314
440
  dependencies = [
@@ -322,9 +448,9 @@ def check_dependencies() -> bool:
322
448
  ("watchdog", "Watchdog"),
323
449
  ("loguru", "Loguru"),
324
450
  ]
325
-
451
+
326
452
  all_available = True
327
-
453
+
328
454
  for module_name, display_name in dependencies:
329
455
  try:
330
456
  __import__(module_name)
@@ -332,7 +458,7 @@ def check_dependencies() -> bool:
332
458
  except ImportError:
333
459
  print_dependency_status(display_name, False)
334
460
  all_available = False
335
-
461
+
336
462
  return all_available
337
463
 
338
464
 
@@ -4,7 +4,6 @@ import asyncio
4
4
  import signal
5
5
  import sys
6
6
  from pathlib import Path
7
- from typing import Optional
8
7
 
9
8
  import typer
10
9
  from loguru import logger
@@ -36,7 +35,7 @@ def watch_main(
36
35
  dir_okay=True,
37
36
  readable=True,
38
37
  ),
39
- config: Optional[Path] = typer.Option(
38
+ config: Path | None = typer.Option(
40
39
  None,
41
40
  "--config",
42
41
  "-c",
@@ -54,19 +53,19 @@ def watch_main(
54
53
  ),
55
54
  ) -> None:
56
55
  """Watch for file changes and automatically update the search index.
57
-
56
+
58
57
  This command starts a file watcher that monitors your project directory
59
58
  for changes to code files. When files are created, modified, or deleted,
60
59
  the search index is automatically updated to reflect the changes.
61
-
60
+
62
61
  The watcher will:
63
62
  - Monitor all files with configured extensions
64
63
  - Debounce rapid changes to avoid excessive indexing
65
64
  - Update the index incrementally for better performance
66
65
  - Ignore common build/cache directories
67
-
66
+
68
67
  Press Ctrl+C to stop watching.
69
-
68
+
70
69
  Examples:
71
70
  mcp-vector-search watch
72
71
  mcp-vector-search watch /path/to/project --verbose
@@ -75,7 +74,7 @@ def watch_main(
75
74
  if verbose:
76
75
  logger.remove()
77
76
  logger.add(sys.stderr, level="DEBUG")
78
-
77
+
79
78
  try:
80
79
  asyncio.run(_watch_async(project_root, config))
81
80
  except KeyboardInterrupt:
@@ -85,7 +84,7 @@ def watch_main(
85
84
  raise typer.Exit(1)
86
85
 
87
86
 
88
- async def _watch_async(project_root: Path, config_path: Optional[Path]) -> None:
87
+ async def _watch_async(project_root: Path, config_path: Path | None) -> None:
89
88
  """Async implementation of watch command."""
90
89
  # Load project configuration
91
90
  try:
@@ -96,17 +95,17 @@ async def _watch_async(project_root: Path, config_path: Optional[Path]) -> None:
96
95
  "Run 'mcp-vector-search init' first."
97
96
  )
98
97
  raise typer.Exit(1)
99
-
98
+
100
99
  config = project_manager.load_config()
101
100
  print_info(f"Loaded configuration from {project_root}")
102
-
101
+
103
102
  except ProjectNotFoundError:
104
103
  print_error(
105
104
  f"No MCP Vector Search project found at {project_root}. "
106
105
  "Run 'mcp-vector-search init' to initialize."
107
106
  )
108
107
  raise typer.Exit(1)
109
-
108
+
110
109
  # Setup database and indexer
111
110
  try:
112
111
  embedding_function, _ = create_embedding_function(config.embedding_model)
@@ -114,19 +113,19 @@ async def _watch_async(project_root: Path, config_path: Optional[Path]) -> None:
114
113
  persist_directory=config.index_path,
115
114
  embedding_function=embedding_function,
116
115
  )
117
-
116
+
118
117
  indexer = SemanticIndexer(
119
118
  database=database,
120
119
  project_root=project_root,
121
120
  file_extensions=config.file_extensions,
122
121
  )
123
-
122
+
124
123
  print_info(f"Initialized database at {config.index_path}")
125
-
124
+
126
125
  except Exception as e:
127
126
  print_error(f"Failed to initialize database: {e}")
128
127
  raise typer.Exit(1)
129
-
128
+
130
129
  # Start watching
131
130
  try:
132
131
  async with database:
@@ -136,34 +135,34 @@ async def _watch_async(project_root: Path, config_path: Optional[Path]) -> None:
136
135
  indexer=indexer,
137
136
  database=database,
138
137
  )
139
-
138
+
140
139
  print_success("🔍 Starting file watcher...")
141
140
  print_info(f"📁 Watching: {project_root}")
142
141
  print_info(f"📄 Extensions: {', '.join(config.file_extensions)}")
143
142
  print_info("Press Ctrl+C to stop watching")
144
-
143
+
145
144
  async with watcher:
146
145
  # Set up signal handlers for graceful shutdown
147
146
  stop_event = asyncio.Event()
148
-
147
+
149
148
  def signal_handler():
150
149
  print_info("\n⏹️ Stopping file watcher...")
151
150
  stop_event.set()
152
-
151
+
153
152
  # Handle SIGINT (Ctrl+C) and SIGTERM
154
153
  if sys.platform != "win32":
155
154
  loop = asyncio.get_running_loop()
156
155
  for sig in (signal.SIGINT, signal.SIGTERM):
157
156
  loop.add_signal_handler(sig, signal_handler)
158
-
157
+
159
158
  try:
160
159
  # Wait for stop signal
161
160
  await stop_event.wait()
162
161
  except KeyboardInterrupt:
163
162
  signal_handler()
164
-
163
+
165
164
  print_success("✅ File watcher stopped")
166
-
165
+
167
166
  except Exception as e:
168
167
  print_error(f"File watching failed: {e}")
169
168
  raise typer.Exit(1)
@@ -181,7 +180,7 @@ def watch_status(
181
180
  ),
182
181
  ) -> None:
183
182
  """Check if file watching is enabled for a project.
184
-
183
+
185
184
  This command checks the project configuration to see if file watching
186
185
  is enabled and provides information about the watch settings.
187
186
  """
@@ -190,20 +189,20 @@ def watch_status(
190
189
  if not project_manager.is_initialized():
191
190
  print_error(f"Project not initialized at {project_root}")
192
191
  raise typer.Exit(1)
193
-
192
+
194
193
  config = project_manager.load_config()
195
-
194
+
196
195
  console.print("\n[bold]File Watch Status[/bold]")
197
196
  console.print(f"Project: {project_root}")
198
197
  console.print(f"Watch Files: {'✓' if config.watch_files else '✗'}")
199
-
198
+
200
199
  if config.watch_files:
201
200
  console.print(f"Extensions: {', '.join(config.file_extensions)}")
202
201
  print_info("File watching is enabled for this project")
203
202
  else:
204
203
  print_warning("File watching is disabled for this project")
205
204
  print_info("Enable with: mcp-vector-search config set watch_files true")
206
-
205
+
207
206
  except ProjectNotFoundError:
208
207
  print_error(f"No MCP Vector Search project found at {project_root}")
209
208
  raise typer.Exit(1)
@@ -224,7 +223,7 @@ def watch_enable(
224
223
  ),
225
224
  ) -> None:
226
225
  """Enable file watching for a project.
227
-
226
+
228
227
  This command enables the watch_files setting in the project configuration.
229
228
  After enabling, you can use 'mcp-vector-search watch' to start monitoring.
230
229
  """
@@ -233,14 +232,14 @@ def watch_enable(
233
232
  if not project_manager.is_initialized():
234
233
  print_error(f"Project not initialized at {project_root}")
235
234
  raise typer.Exit(1)
236
-
235
+
237
236
  config = project_manager.load_config()
238
237
  config.watch_files = True
239
238
  project_manager.save_config(config)
240
-
239
+
241
240
  print_success("✅ File watching enabled")
242
241
  print_info("Start watching with: mcp-vector-search watch")
243
-
242
+
244
243
  except ProjectNotFoundError:
245
244
  print_error(f"No MCP Vector Search project found at {project_root}")
246
245
  raise typer.Exit(1)
@@ -261,7 +260,7 @@ def watch_disable(
261
260
  ),
262
261
  ) -> None:
263
262
  """Disable file watching for a project.
264
-
263
+
265
264
  This command disables the watch_files setting in the project configuration.
266
265
  """
267
266
  try:
@@ -269,13 +268,13 @@ def watch_disable(
269
268
  if not project_manager.is_initialized():
270
269
  print_error(f"Project not initialized at {project_root}")
271
270
  raise typer.Exit(1)
272
-
271
+
273
272
  config = project_manager.load_config()
274
273
  config.watch_files = False
275
274
  project_manager.save_config(config)
276
-
275
+
277
276
  print_success("✅ File watching disabled")
278
-
277
+
279
278
  except ProjectNotFoundError:
280
279
  print_error(f"No MCP Vector Search project found at {project_root}")
281
280
  raise typer.Exit(1)