from __future__ import annotations import os from pathlib import Path from typing import Iterable TEXT_EXTENSIONS = { ".py", ".js", ".jsx", ".ts", ".tsx", ".java", ".go", ".rb", ".php", ".cs", ".cpp", ".c", ".rs", ".swift", ".kt", ".scala", ".vue", ".svelte", ".html", ".css", ".scss", ".json", ".yaml", ".yml", ".toml", ".md", ".env.example", } def to_posix_path(path: str | os.PathLike[str]) -> str: return Path(path).as_posix() def is_text_file(path: str, size: int) -> bool: return Path(path).suffix.lower() in TEXT_EXTENSIONS and size <= 100_000 def safe_read_text(path: str, max_chars: int = 100_000) -> str: try: with open(path, "r", encoding="utf-8", errors="ignore") as file: return file.read(max_chars) except OSError: return "" def count_lines(text: str) -> int: if not text: return 0 return text.count("\n") + (0 if text.endswith("\n") else 1) def has_any_token(value: str, tokens: Iterable[str]) -> bool: lower = value.lower() return any(token.lower() in lower for token in tokens)