diff --git a/codex-lens/src/codexlens/cli/commands.py b/codex-lens/src/codexlens/cli/commands.py index 166237d2..4fe396b1 100644 --- a/codex-lens/src/codexlens/cli/commands.py +++ b/codex-lens/src/codexlens/cli/commands.py @@ -248,12 +248,6 @@ def init( else: console.print(f"[red]Init failed:[/red] {exc}") raise typer.Exit(code=1) - except Exception as exc: - if json_mode: - print_json(success=False, error=f"Unexpected error: {exc}") - else: - console.print(f"[red]Init failed (unexpected):[/red] {exc}") - raise typer.Exit(code=1) finally: if registry is not None: registry.close() @@ -457,12 +451,6 @@ def search( else: console.print(f"[red]Search failed:[/red] {exc}") raise typer.Exit(code=1) - except Exception as exc: - if json_mode: - print_json(success=False, error=f"Unexpected error: {exc}") - else: - console.print(f"[red]Search failed (unexpected):[/red] {exc}") - raise typer.Exit(code=1) finally: if registry is not None: registry.close() @@ -522,12 +510,6 @@ def symbol( else: console.print(f"[red]Symbol lookup failed:[/red] {exc}") raise typer.Exit(code=1) - except Exception as exc: - if json_mode: - print_json(success=False, error=f"Unexpected error: {exc}") - else: - console.print(f"[red]Symbol lookup failed (unexpected):[/red] {exc}") - raise typer.Exit(code=1) finally: if registry is not None: registry.close() @@ -583,12 +565,6 @@ def inspect( else: console.print(f"[red]Inspect failed:[/red] {exc}") raise typer.Exit(code=1) - except Exception as exc: - if json_mode: - print_json(success=False, error=f"Unexpected error: {exc}") - else: - console.print(f"[red]Inspect failed (unexpected):[/red] {exc}") - raise typer.Exit(code=1) @app.command() @@ -727,12 +703,6 @@ def status( else: console.print(f"[red]Status failed:[/red] {exc}") raise typer.Exit(code=1) - except Exception as exc: - if json_mode: - print_json(success=False, error=f"Unexpected error: {exc}") - else: - console.print(f"[red]Status failed (unexpected):[/red] {exc}") - raise typer.Exit(code=1) finally: if registry is not None: registry.close() @@ -884,12 +854,6 @@ def projects( else: console.print(f"[red]Projects command failed:[/red] {exc}") raise typer.Exit(code=1) - except Exception as exc: - if json_mode: - print_json(success=False, error=f"Unexpected error: {exc}") - else: - console.print(f"[red]Projects command failed (unexpected):[/red] {exc}") - raise typer.Exit(code=1) finally: if registry is not None: registry.close() @@ -1060,12 +1024,6 @@ def config( else: console.print(f"[red]Config command failed:[/red] {exc}") raise typer.Exit(code=1) - except Exception as exc: - if json_mode: - print_json(success=False, error=f"Unexpected error: {exc}") - else: - console.print(f"[red]Config command failed (unexpected):[/red] {exc}") - raise typer.Exit(code=1) @app.command() @@ -1192,12 +1150,6 @@ def migrate( else: console.print(f"[red]Migration failed:[/red] {exc}") raise typer.Exit(code=1) - except Exception as exc: - if json_mode: - print_json(success=False, error=f"Unexpected error: {exc}") - else: - console.print(f"[red]Migration failed (unexpected):[/red] {exc}") - raise typer.Exit(code=1) finally: if registry is not None: registry.close() @@ -1346,12 +1298,6 @@ def clean( else: console.print(f"[red]Clean failed:[/red] {exc}") raise typer.Exit(code=1) - except Exception as exc: - if json_mode: - print_json(success=False, error=f"Unexpected error: {exc}") - else: - console.print(f"[red]Clean failed (unexpected):[/red] {exc}") - raise typer.Exit(code=1) @app.command("semantic-list") @@ -1472,12 +1418,6 @@ def semantic_list( else: console.print(f"[red]Semantic-list failed:[/red] {exc}") raise typer.Exit(code=1) - except Exception as exc: - if json_mode: - print_json(success=False, error=f"Unexpected error: {exc}") - else: - console.print(f"[red]Semantic-list failed (unexpected):[/red] {exc}") - raise typer.Exit(code=1) finally: if registry is not None: registry.close() @@ -1549,12 +1489,6 @@ def model_list( console.print("[red]Error:[/red] fastembed not installed") console.print("[yellow]Install with:[/yellow] pip install codexlens[semantic]") raise typer.Exit(code=1) - except Exception as exc: - if json_mode: - print_json(success=False, error=str(exc)) - else: - console.print(f"[red]Model-list failed:[/red] {exc}") - raise typer.Exit(code=1) @app.command(name="model-download") @@ -1600,12 +1534,6 @@ def model_download( console.print("[red]Error:[/red] fastembed not installed") console.print("[yellow]Install with:[/yellow] pip install codexlens[semantic]") raise typer.Exit(code=1) - except Exception as exc: - if json_mode: - print_json(success=False, error=str(exc)) - else: - console.print(f"[red]Model-download failed:[/red] {exc}") - raise typer.Exit(code=1) @app.command(name="model-delete") @@ -1639,13 +1567,6 @@ def model_delete( console.print(f" Model: {data['model_name']}") console.print(f" Freed space: {data['deleted_size_mb']:.1f} MB") - except Exception as exc: - if json_mode: - print_json(success=False, error=str(exc)) - else: - console.print(f"[red]Model-delete failed:[/red] {exc}") - raise typer.Exit(code=1) - @app.command(name="model-info") def model_info( @@ -1682,13 +1603,6 @@ def model_info( console.print(f"\n Description: {data['description']}") console.print(f" Use case: {data['use_case']}") - except Exception as exc: - if json_mode: - print_json(success=False, error=str(exc)) - else: - console.print(f"[red]Model-info failed:[/red] {exc}") - raise typer.Exit(code=1) - # ==================== Embedding Management Commands ==================== @@ -1821,13 +1735,6 @@ def embeddings_status( console.print("\n[dim]Generate embeddings with:[/dim]") console.print(f" [cyan]codexlens embeddings-generate {index_path}[/cyan]") - except Exception as exc: - if json_mode: - print_json(success=False, error=str(exc)) - else: - console.print(f"[red]Embeddings-status failed:[/red] {exc}") - raise typer.Exit(code=1) - @app.command(name="embeddings-generate") def embeddings_generate( @@ -2011,10 +1918,3 @@ def embeddings_generate( console.print("\n[dim]Use vector search with:[/dim]") console.print(" [cyan]codexlens search 'your query' --mode pure-vector[/cyan]") - - except Exception as exc: - if json_mode: - print_json(success=False, error=str(exc)) - else: - console.print(f"[red]Embeddings-generate failed:[/red] {exc}") - raise typer.Exit(code=1) diff --git a/codex-lens/src/codexlens/config.py b/codex-lens/src/codexlens/config.py index fb96c74d..953b6ac7 100644 --- a/codex-lens/src/codexlens/config.py +++ b/codex-lens/src/codexlens/config.py @@ -4,6 +4,7 @@ from __future__ import annotations import os from dataclasses import dataclass, field +from functools import cached_property from pathlib import Path from typing import Any, Dict, List, Optional @@ -96,17 +97,17 @@ class Config: except Exception as exc: raise ConfigError(f"Failed to initialize data_dir at {self.data_dir}: {exc}") from exc - @property + @cached_property def cache_dir(self) -> Path: """Directory for transient caches.""" return self.data_dir / "cache" - @property + @cached_property def index_dir(self) -> Path: """Directory where index artifacts are stored.""" return self.data_dir / "index" - @property + @cached_property def db_path(self) -> Path: """Default SQLite index path.""" return self.index_dir / "codexlens.db" diff --git a/codex-lens/src/codexlens/entities.py b/codex-lens/src/codexlens/entities.py index a69edfa1..08e51f54 100644 --- a/codex-lens/src/codexlens/entities.py +++ b/codex-lens/src/codexlens/entities.py @@ -2,6 +2,7 @@ from __future__ import annotations +from enum import Enum from typing import Any, Dict, List, Optional, Tuple from pydantic import BaseModel, Field, field_validator @@ -62,24 +63,23 @@ class IndexedFile(BaseModel): return cleaned +class RelationshipType(str, Enum): + """Types of code relationships.""" + CALL = "call" + INHERITS = "inherits" + IMPORTS = "imports" + + class CodeRelationship(BaseModel): """A relationship between code symbols (e.g., function calls, inheritance).""" source_symbol: str = Field(..., min_length=1, description="Name of source symbol") target_symbol: str = Field(..., min_length=1, description="Name of target symbol") - relationship_type: str = Field(..., min_length=1, description="Type of relationship (call, inherits, etc.)") + relationship_type: RelationshipType = Field(..., description="Type of relationship (call, inherits, etc.)") source_file: str = Field(..., min_length=1, description="File path containing source symbol") target_file: Optional[str] = Field(default=None, description="File path containing target (None if same file)") source_line: int = Field(..., ge=1, description="Line number where relationship occurs (1-based)") - @field_validator("relationship_type") - @classmethod - def validate_relationship_type(cls, value: str) -> str: - allowed_types = {"call", "inherits", "imports"} - if value not in allowed_types: - raise ValueError(f"relationship_type must be one of {allowed_types}") - return value - class AdditionalLocation(BaseModel): """A pointer to another location where a similar result was found.