"""Configuration and settings."""
import hashlib
from pathlib import Path
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
"""Application settings with environment variable support."""
model_config = SettingsConfigDict(
env_prefix="SEMANTIC_CODE_MCP_",
env_file=".env",
extra="ignore",
)
# Embedding settings
embedding_model: str = "all-MiniLM-L6-v2"
embedding_device: str = "auto"
# Storage settings
cache_dir: Path = Field(default_factory=lambda: Path.home() / ".cache" / "semantic-code-mcp")
local_index: bool = False
# Chunking settings
chunking_target_tokens: int = 800
chunking_max_tokens: int = 1500
# Logging
debug: bool = False # Enable debug logging (SEMANTIC_CODE_MCP_DEBUG=1)
# Profiling (dev only)
profile: bool = False # Enable profiling (SEMANTIC_CODE_MCP_PROFILE=1)
# Ignore patterns
ignore_patterns: list[str] = Field(
default_factory=lambda: [
"node_modules/**",
".venv/**",
"__pycache__/**",
".git/**",
"*.pyc",
".pytest_cache/**",
]
)
use_gitignore: bool = True
_settings: Settings | None = None
def configure_settings(settings: Settings) -> None:
"""Set the global settings instance (call once at startup or in tests)."""
global _settings
_settings = settings
def get_settings() -> Settings:
"""Get the global settings, auto-creating from env if not configured."""
global _settings
if _settings is None:
_settings = Settings()
return _settings
def get_index_path(settings: Settings, project_path: Path) -> Path:
"""Get the index storage path for a project.
Args:
settings: Application settings.
project_path: Path to the project root.
Returns:
Path where the index should be stored.
"""
if settings.local_index:
return project_path / ".semantic-code"
# Hash the absolute path for global cache
path_hash = hashlib.sha256(str(project_path.resolve()).encode()).hexdigest()[:16]
return settings.cache_dir / path_hash
def resolve_cache_dir(settings: Settings, project_path: Path, override: Path | None = None) -> Path:
"""Get cache directory, with optional override (e.g. for tests).
Args:
settings: Application settings.
project_path: Path to the project root.
override: Explicit cache directory (bypasses settings).
Returns:
Resolved cache directory, created if it doesn't exist.
"""
if override is not None:
override.mkdir(parents=True, exist_ok=True)
return override
path = get_index_path(settings, project_path)
path.mkdir(parents=True, exist_ok=True)
return path