mcp-vector-search 0.4.13__py3-none-any.whl → 0.5.0__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 (29) 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 +171 -52
  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 +6 -0
  19. mcp_vector_search/parsers/dart.py +605 -0
  20. mcp_vector_search/parsers/php.py +694 -0
  21. mcp_vector_search/parsers/registry.py +16 -1
  22. mcp_vector_search/parsers/ruby.py +678 -0
  23. mcp_vector_search/parsers/text.py +31 -25
  24. mcp_vector_search/utils/gitignore.py +72 -71
  25. {mcp_vector_search-0.4.13.dist-info → mcp_vector_search-0.5.0.dist-info}/METADATA +59 -2
  26. {mcp_vector_search-0.4.13.dist-info → mcp_vector_search-0.5.0.dist-info}/RECORD +29 -26
  27. {mcp_vector_search-0.4.13.dist-info → mcp_vector_search-0.5.0.dist-info}/WHEEL +0 -0
  28. {mcp_vector_search-0.4.13.dist-info → mcp_vector_search-0.5.0.dist-info}/entry_points.txt +0 -0
  29. {mcp_vector_search-0.4.13.dist-info → mcp_vector_search-0.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -45,23 +45,23 @@ def reset_index(
45
45
  ),
46
46
  ) -> None:
47
47
  """Reset the vector search index (clear corrupted data).
48
-
48
+
49
49
  This command will:
50
50
  - Create a backup of the current index (unless --no-backup)
51
51
  - Clear the entire vector database
52
52
  - Preserve your configuration settings
53
-
53
+
54
54
  After reset, run 'mcp-vector-search index' to rebuild.
55
55
  """
56
56
  root = project_root or Path.cwd()
57
-
57
+
58
58
  try:
59
59
  # Check if project is initialized
60
60
  project_manager = ProjectManager(root)
61
61
  if not project_manager.is_initialized():
62
62
  print_error("Project not initialized. Run 'mcp-vector-search init' first.")
63
63
  raise typer.Exit(1)
64
-
64
+
65
65
  # Get confirmation unless forced
66
66
  if not force:
67
67
  console.print(
@@ -77,28 +77,29 @@ def reset_index(
77
77
  border_style="red",
78
78
  )
79
79
  )
80
-
80
+
81
81
  if not Confirm.ask("\nDo you want to proceed?", default=False):
82
82
  console.print("[yellow]Reset cancelled[/yellow]")
83
83
  raise typer.Exit(0)
84
-
84
+
85
85
  # Get the database directory
86
86
  config = project_manager.load_config()
87
87
  db_path = root / ".mcp_vector_search" / "db"
88
-
88
+
89
89
  if not db_path.exists():
90
90
  print_warning("No index found. Nothing to reset.")
91
91
  raise typer.Exit(0)
92
-
92
+
93
93
  # Create backup if requested
94
94
  if backup:
95
95
  backup_dir = root / ".mcp_vector_search" / "backups"
96
96
  backup_dir.mkdir(exist_ok=True)
97
-
97
+
98
98
  import time
99
+
99
100
  timestamp = int(time.time())
100
101
  backup_path = backup_dir / f"db_backup_{timestamp}"
101
-
102
+
102
103
  try:
103
104
  shutil.copytree(db_path, backup_path)
104
105
  print_success(f"Created backup at: {backup_path.relative_to(root)}")
@@ -108,7 +109,7 @@ def reset_index(
108
109
  if not Confirm.ask("Continue without backup?", default=False):
109
110
  console.print("[yellow]Reset cancelled[/yellow]")
110
111
  raise typer.Exit(0)
111
-
112
+
112
113
  # Clear the index
113
114
  console.print("[cyan]Clearing index...[/cyan]")
114
115
  try:
@@ -118,7 +119,7 @@ def reset_index(
118
119
  except Exception as e:
119
120
  print_error(f"Failed to clear index: {e}")
120
121
  raise typer.Exit(1)
121
-
122
+
122
123
  # Show next steps
123
124
  console.print(
124
125
  Panel(
@@ -130,7 +131,7 @@ def reset_index(
130
131
  border_style="green",
131
132
  )
132
133
  )
133
-
134
+
134
135
  except (DatabaseError, IndexCorruptionError) as e:
135
136
  print_error(f"Reset failed: {e}")
136
137
  raise typer.Exit(1)
@@ -156,12 +157,12 @@ def reset_all(
156
157
  ),
157
158
  ) -> None:
158
159
  """Reset everything (index and configuration).
159
-
160
+
160
161
  This will completely remove all MCP Vector Search data,
161
162
  requiring re-initialization with 'mcp-vector-search init'.
162
163
  """
163
164
  root = project_root or Path.cwd()
164
-
165
+
165
166
  # Get confirmation unless forced
166
167
  if not force:
167
168
  console.print(
@@ -177,28 +178,28 @@ def reset_all(
177
178
  border_style="red",
178
179
  )
179
180
  )
180
-
181
+
181
182
  if not Confirm.ask("\nAre you absolutely sure?", default=False):
182
183
  console.print("[yellow]Reset cancelled[/yellow]")
183
184
  raise typer.Exit(0)
184
-
185
+
185
186
  # Double confirmation for destructive action
186
187
  if not Confirm.ask("Type 'yes' to confirm complete reset", default=False):
187
188
  console.print("[yellow]Reset cancelled[/yellow]")
188
189
  raise typer.Exit(0)
189
-
190
+
190
191
  # Remove entire .mcp_vector_search directory
191
192
  mcp_dir = root / ".mcp_vector_search"
192
-
193
+
193
194
  if not mcp_dir.exists():
194
195
  print_warning("No MCP Vector Search data found. Nothing to reset.")
195
196
  raise typer.Exit(0)
196
-
197
+
197
198
  console.print("[cyan]Removing all MCP Vector Search data...[/cyan]")
198
199
  try:
199
200
  shutil.rmtree(mcp_dir)
200
201
  print_success("All data removed successfully!")
201
-
202
+
202
203
  console.print(
203
204
  Panel(
204
205
  "[green]✅ Complete reset done![/green]\n\n"
@@ -229,7 +230,7 @@ async def check_health(
229
230
  ),
230
231
  ) -> None:
231
232
  """Check the health of the search index.
232
-
233
+
233
234
  This command will:
234
235
  - Verify database connectivity
235
236
  - Check for index corruption
@@ -237,49 +238,47 @@ async def check_health(
237
238
  - Optionally attempt repairs with --fix
238
239
  """
239
240
  root = project_root or Path.cwd()
240
-
241
+
241
242
  try:
242
243
  # Check if project is initialized
243
244
  project_manager = ProjectManager(root)
244
245
  if not project_manager.is_initialized():
245
246
  print_error("Project not initialized. Run 'mcp-vector-search init' first.")
246
247
  raise typer.Exit(1)
247
-
248
+
248
249
  console.print("[cyan]Performing health check...[/cyan]\n")
249
-
250
+
250
251
  # Initialize database
252
+ from ...config.defaults import get_default_cache_path
251
253
  from ...core.database import ChromaVectorDatabase
252
254
  from ...core.embeddings import create_embedding_function
253
- from ...config.defaults import get_default_cache_path
254
-
255
+
255
256
  config = project_manager.load_config()
256
257
  db_path = root / ".mcp_vector_search" / "db"
257
-
258
+
258
259
  # Setup embedding function and cache
259
- cache_dir = (
260
- get_default_cache_path(root) if config.cache_embeddings else None
261
- )
260
+ cache_dir = get_default_cache_path(root) if config.cache_embeddings else None
262
261
  embedding_function, _ = create_embedding_function(
263
262
  model_name=config.embedding_model,
264
263
  cache_dir=cache_dir,
265
264
  cache_size=config.max_cache_size,
266
265
  )
267
-
266
+
268
267
  # Create database instance
269
268
  db = ChromaVectorDatabase(
270
269
  persist_directory=db_path,
271
270
  embedding_function=embedding_function,
272
271
  )
273
-
272
+
274
273
  # Initialize and check health
275
274
  try:
276
275
  await db.initialize()
277
276
  is_healthy = await db.health_check()
278
-
277
+
279
278
  if is_healthy:
280
279
  # Get stats for additional info
281
280
  stats = await db.get_stats()
282
-
281
+
283
282
  console.print(
284
283
  Panel(
285
284
  f"[green]✅ Index is healthy![/green]\n\n"
@@ -304,14 +303,14 @@ async def check_health(
304
303
  border_style="red",
305
304
  )
306
305
  )
307
-
306
+
308
307
  if fix:
309
308
  console.print("\n[cyan]Attempting to repair index...[/cyan]")
310
309
  # The health check already attempts recovery
311
310
  # Try to reinitialize
312
311
  await db.close()
313
312
  await db.initialize()
314
-
313
+
315
314
  # Check again
316
315
  is_healthy = await db.health_check()
317
316
  if is_healthy:
@@ -328,7 +327,7 @@ async def check_health(
328
327
  "or 'mcp-vector-search reset index' to clear and rebuild."
329
328
  )
330
329
  raise typer.Exit(1)
331
-
330
+
332
331
  except IndexCorruptionError as e:
333
332
  console.print(
334
333
  Panel(
@@ -342,10 +341,10 @@ async def check_health(
342
341
  )
343
342
  )
344
343
  raise typer.Exit(1)
345
-
344
+
346
345
  finally:
347
346
  await db.close()
348
-
347
+
349
348
  except Exception as e:
350
349
  logger.error(f"Health check error: {e}")
351
350
  print_error(f"Health check failed: {e}")
@@ -375,6 +374,7 @@ def reset_main(ctx: typer.Context) -> None:
375
374
  # Export for backwards compatibility
376
375
  main = reset_main
377
376
 
377
+
378
378
  # Make health check synchronous for CLI
379
379
  def health_main(
380
380
  project_root: Path = typer.Option(
@@ -390,4 +390,4 @@ def health_main(
390
390
  ),
391
391
  ) -> None:
392
392
  """Check the health of the search index (sync wrapper)."""
393
- asyncio.run(check_health(project_root, fix))
393
+ asyncio.run(check_health(project_root, fix))
@@ -14,7 +14,9 @@ from ...core.search import SemanticSearchEngine
14
14
  from ..didyoumean import create_enhanced_typer
15
15
  from ..output import (
16
16
  print_error,
17
+ print_info,
17
18
  print_search_results,
19
+ print_tip,
18
20
  )
19
21
 
20
22
  # Create search subcommand app with "did you mean" functionality (kept for backward compatibility)
@@ -23,7 +25,7 @@ search_app = create_enhanced_typer(help="Search code semantically")
23
25
 
24
26
  def search_main(
25
27
  ctx: typer.Context,
26
- query: str = typer.Argument(..., help="Search query"),
28
+ query: str = typer.Argument(..., help="Search query or file path (for --similar)"),
27
29
  project_root: Path | None = typer.Option(
28
30
  None,
29
31
  "--project-root",
@@ -33,6 +35,7 @@ def search_main(
33
35
  file_okay=False,
34
36
  dir_okay=True,
35
37
  readable=True,
38
+ rich_help_panel="🔧 Global Options",
36
39
  ),
37
40
  limit: int = typer.Option(
38
41
  10,
@@ -41,27 +44,32 @@ def search_main(
41
44
  help="Maximum number of results to return",
42
45
  min=1,
43
46
  max=100,
47
+ rich_help_panel="📊 Result Options",
44
48
  ),
45
49
  files: str | None = typer.Option(
46
50
  None,
47
51
  "--files",
48
52
  "-f",
49
53
  help="Filter by file patterns (e.g., '*.py' or 'src/*.js')",
54
+ rich_help_panel="🔍 Filters",
50
55
  ),
51
56
  language: str | None = typer.Option(
52
57
  None,
53
58
  "--language",
54
- help="Filter by programming language",
59
+ help="Filter by programming language (python, javascript, typescript)",
60
+ rich_help_panel="🔍 Filters",
55
61
  ),
56
62
  function_name: str | None = typer.Option(
57
63
  None,
58
64
  "--function",
59
65
  help="Filter by function name",
66
+ rich_help_panel="🔍 Filters",
60
67
  ),
61
68
  class_name: str | None = typer.Option(
62
69
  None,
63
70
  "--class",
64
71
  help="Filter by class name",
72
+ rich_help_panel="🔍 Filters",
65
73
  ),
66
74
  similarity_threshold: float | None = typer.Option(
67
75
  None,
@@ -70,57 +78,87 @@ def search_main(
70
78
  help="Minimum similarity threshold (0.0 to 1.0)",
71
79
  min=0.0,
72
80
  max=1.0,
81
+ rich_help_panel="🎯 Search Options",
73
82
  ),
74
83
  similar: bool = typer.Option(
75
84
  False,
76
85
  "--similar",
77
86
  help="Find code similar to the query (treats query as file path)",
87
+ rich_help_panel="🎯 Search Options",
78
88
  ),
79
89
  context: bool = typer.Option(
80
90
  False,
81
91
  "--context",
82
92
  help="Search for code based on contextual description",
93
+ rich_help_panel="🎯 Search Options",
83
94
  ),
84
95
  focus: str | None = typer.Option(
85
96
  None,
86
97
  "--focus",
87
98
  help="Focus areas for context search (comma-separated)",
99
+ rich_help_panel="🎯 Search Options",
88
100
  ),
89
101
  no_content: bool = typer.Option(
90
102
  False,
91
103
  "--no-content",
92
104
  help="Don't show code content in results",
105
+ rich_help_panel="📊 Result Options",
93
106
  ),
94
107
  json_output: bool = typer.Option(
95
108
  False,
96
109
  "--json",
97
110
  help="Output results in JSON format",
111
+ rich_help_panel="📊 Result Options",
98
112
  ),
99
113
  export_format: str | None = typer.Option(
100
114
  None,
101
115
  "--export",
102
116
  help="Export results to file (json, csv, markdown, summary)",
117
+ rich_help_panel="💾 Export Options",
103
118
  ),
104
119
  export_path: Path | None = typer.Option(
105
120
  None,
106
121
  "--export-path",
107
122
  help="Custom export file path",
123
+ rich_help_panel="💾 Export Options",
108
124
  ),
109
125
  ) -> None:
110
- """Search your codebase semantically.
126
+ """🔍 Search your codebase semantically.
111
127
 
112
- This command performs semantic search across your indexed codebase,
113
- finding code that is conceptually similar to your query even if it
114
- doesn't contain the exact keywords.
128
+ Performs vector similarity search across your indexed code to find relevant
129
+ functions, classes, and patterns based on semantic meaning, not just keywords.
115
130
 
116
- Examples:
117
- mcp-vector-search search "authentication middleware"
118
- mcp-vector-search search "database connection" --language python
119
- mcp-vector-search search "error handling" --files "*.js" --limit 5
120
- mcp-vector-search search "user validation" --function validate --json
121
- mcp-vector-search search "src/auth.py" --similar
122
- mcp-vector-search search "implement rate limiting" --context
123
- mcp-vector-search search "user authentication" --context --focus security,middleware
131
+ [bold cyan]Basic Search Examples:[/bold cyan]
132
+
133
+ [green]Simple semantic search:[/green]
134
+ $ mcp-vector-search search "authentication middleware"
135
+
136
+ [green]Search with language filter:[/green]
137
+ $ mcp-vector-search search "database connection" --language python
138
+
139
+ [green]Limit results:[/green]
140
+ $ mcp-vector-search search "error handling" --limit 5
141
+
142
+ [bold cyan]Advanced Search:[/bold cyan]
143
+
144
+ [green]Filter by file pattern:[/green]
145
+ $ mcp-vector-search search "validation" --files "src/*.py"
146
+
147
+ [green]Find similar code:[/green]
148
+ $ mcp-vector-search search "src/auth.py" --similar
149
+
150
+ [green]Context-based search:[/green]
151
+ $ mcp-vector-search search "implement rate limiting" --context --focus security
152
+
153
+ [bold cyan]Export Results:[/bold cyan]
154
+
155
+ [green]Export to JSON:[/green]
156
+ $ mcp-vector-search search "api endpoints" --export json
157
+
158
+ [green]Export to markdown:[/green]
159
+ $ mcp-vector-search search "utils" --export markdown
160
+
161
+ [dim]💡 Tip: Use quotes for multi-word queries. Adjust --threshold for more/fewer results.[/dim]
124
162
  """
125
163
  try:
126
164
  project_root = project_root or ctx.obj.get("project_root") or Path.cwd()
@@ -303,6 +341,27 @@ async def run_search(
303
341
  show_content=show_content,
304
342
  )
305
343
 
344
+ # Add contextual tips based on results
345
+ if results:
346
+ if len(results) >= limit:
347
+ print_tip(
348
+ f"More results may be available. Use [cyan]--limit {limit * 2}[/cyan] to see more."
349
+ )
350
+ if not export_format:
351
+ print_tip(
352
+ "Export results with [cyan]--export json[/cyan] or [cyan]--export markdown[/cyan]"
353
+ )
354
+ else:
355
+ # No results - provide helpful suggestions
356
+ print_info("\n[bold]No results found. Try:[/bold]")
357
+ print_info(" • Use more general terms in your query")
358
+ print_info(
359
+ " • Lower the similarity threshold with [cyan]--threshold 0.3[/cyan]"
360
+ )
361
+ print_info(
362
+ " • Check if files are indexed with [cyan]mcp-vector-search status[/cyan]"
363
+ )
364
+
306
365
  except Exception as e:
307
366
  logger.error(f"Search execution failed: {e}")
308
367
  raise
@@ -38,39 +38,63 @@ def main(
38
38
  file_okay=False,
39
39
  dir_okay=True,
40
40
  readable=True,
41
+ rich_help_panel="🔧 Global Options",
41
42
  ),
42
43
  verbose: bool = typer.Option(
43
44
  False,
44
45
  "--verbose",
45
46
  "-v",
46
- help="Show detailed information",
47
+ help="Show detailed information including paths and patterns",
48
+ rich_help_panel="📊 Display Options",
47
49
  ),
48
50
  health_check: bool = typer.Option(
49
51
  False,
50
52
  "--health-check",
51
- help="Perform health check of all components",
53
+ help="Perform comprehensive health check of all components",
54
+ rich_help_panel="🔍 Diagnostics",
52
55
  ),
53
56
  mcp: bool = typer.Option(
54
57
  False,
55
58
  "--mcp",
56
59
  help="Check Claude Code MCP integration status",
60
+ rich_help_panel="🔍 Diagnostics",
57
61
  ),
58
62
  json_output: bool = typer.Option(
59
63
  False,
60
64
  "--json",
61
65
  help="Output status in JSON format",
66
+ rich_help_panel="📊 Display Options",
62
67
  ),
63
68
  ) -> None:
64
- """Show project status and indexing statistics.
69
+ """📊 Show project status and indexing statistics.
65
70
 
66
- This command displays comprehensive information about your MCP Vector Search
67
- project including configuration, indexing status, and system health.
71
+ Displays comprehensive information about your project including configuration,
72
+ indexing statistics, and system health. Use this to verify setup and monitor
73
+ indexing progress.
68
74
 
69
- Examples:
70
- mcp-vector-search status
71
- mcp-vector-search status --verbose
72
- mcp-vector-search status --health-check --json
73
- mcp-vector-search status --mcp
75
+ [bold cyan]Basic Examples:[/bold cyan]
76
+
77
+ [green]Quick status check:[/green]
78
+ $ mcp-vector-search status
79
+
80
+ [green]Detailed status with all information:[/green]
81
+ $ mcp-vector-search status --verbose
82
+
83
+ [green]Check MCP integration:[/green]
84
+ $ mcp-vector-search status --mcp
85
+
86
+ [bold cyan]Diagnostics:[/bold cyan]
87
+
88
+ [green]Full health check:[/green]
89
+ $ mcp-vector-search status --health-check
90
+
91
+ [green]Export status to JSON:[/green]
92
+ $ mcp-vector-search status --json > status.json
93
+
94
+ [green]Combined diagnostics:[/green]
95
+ $ mcp-vector-search status --verbose --health-check --mcp
96
+
97
+ [dim]💡 Tip: Use --health-check to diagnose issues with dependencies or database.[/dim]
74
98
  """
75
99
  try:
76
100
  # Use provided project_root or current working directory
@@ -207,7 +231,9 @@ async def show_status(
207
231
  raise
208
232
 
209
233
 
210
- def _display_status(status_data: dict[str, Any], verbose: bool, mcp: bool = False) -> None:
234
+ def _display_status(
235
+ status_data: dict[str, Any], verbose: bool, mcp: bool = False
236
+ ) -> None:
211
237
  """Display status in human-readable format."""
212
238
  project_data = status_data["project"]
213
239
  config_data = status_data["configuration"]
@@ -287,7 +313,9 @@ def _display_status(status_data: dict[str, Any], verbose: bool, mcp: bool = Fals
287
313
  elif server_status == "not_installed":
288
314
  console.print(f"[red]✗[/red] MCP Server '{server_name}': Not installed")
289
315
  else:
290
- console.print(f"[yellow]⚠[/yellow] MCP Server '{server_name}': {server_status}")
316
+ console.print(
317
+ f"[yellow]⚠[/yellow] MCP Server '{server_name}': {server_status}"
318
+ )
291
319
 
292
320
  if mcp_data.get("project_config"):
293
321
  console.print("[green]✓[/green] Project Configuration: Found")
@@ -370,7 +398,9 @@ async def perform_health_check(project_root: Path, config) -> dict[str, Any]:
370
398
  return health_status
371
399
 
372
400
 
373
- async def check_mcp_integration(project_root: Path, server_name: str = "mcp-vector-search") -> dict[str, Any]:
401
+ async def check_mcp_integration(
402
+ project_root: Path, server_name: str = "mcp-vector-search"
403
+ ) -> dict[str, Any]:
374
404
  """Check MCP integration status."""
375
405
  mcp_status = {
376
406
  "claude_available": False,
@@ -396,14 +426,16 @@ async def check_mcp_integration(project_root: Path, server_name: str = "mcp-vect
396
426
  [claude_cmd, "mcp", "get", server_name],
397
427
  capture_output=True,
398
428
  text=True,
399
- timeout=10
429
+ timeout=10,
400
430
  )
401
431
 
402
432
  if result.returncode == 0:
403
433
  mcp_status["server_status"] = "installed"
404
434
  else:
405
435
  mcp_status["server_status"] = "not_installed"
406
- mcp_status["issues"].append(f"MCP server '{server_name}' not found in Claude Code")
436
+ mcp_status["issues"].append(
437
+ f"MCP server '{server_name}' not found in Claude Code"
438
+ )
407
439
 
408
440
  except subprocess.TimeoutExpired:
409
441
  mcp_status["server_status"] = "timeout"
@@ -418,12 +450,14 @@ async def check_mcp_integration(project_root: Path, server_name: str = "mcp-vect
418
450
  claude_json_path = project_root / ".claude.json"
419
451
  if claude_json_path.exists():
420
452
  try:
421
- with open(claude_json_path, 'r') as f:
453
+ with open(claude_json_path) as f:
422
454
  config = json.load(f)
423
455
  if config.get("mcpServers", {}).get(server_name):
424
456
  mcp_status["project_config"] = True
425
457
  else:
426
- mcp_status["issues"].append(f"MCP server '{server_name}' not found in project .claude.json")
458
+ mcp_status["issues"].append(
459
+ f"MCP server '{server_name}' not found in project .claude.json"
460
+ )
427
461
  except Exception as e:
428
462
  mcp_status["issues"].append(f"Error reading project .claude.json: {e}")
429
463
  else: