# Directory Scanner

Fuente conceptual: OpenViking `openviking/parse/directory_scan.py` (AGPL-3.0)
Reimplementar desde cero. No copiar codigo.

## Funciones a implementar

### 1. scan_directory

- **Dominio:** infra
- **Lang:** Python
- **Purity:** impure (I/O: filesystem traversal)
- **Signature:** `scan_directory(root: str, supported_extensions: set[str] | None = None, ignore_dirs: set[str] | None = None, include: str | None = None, exclude: str | None = None, strict: bool = False) -> DirectoryScanResult`
- **Descripcion:** Recorre un arbol de directorios y clasifica cada archivo como procesable o no soportado. Util para validacion pre-importacion de directorios.
- **Tipos de retorno:**
  ```python
  @dataclass
  class ClassifiedFile:
      path: str          # path absoluto
      rel_path: str      # relativo a root, forward slashes
      classification: str # "processable" | "unsupported"

  @dataclass
  class DirectoryScanResult:
      root: str
      processable: list[ClassifiedFile]
      unsupported: list[ClassifiedFile]
      skipped: list[str]     # "path (reason)"
      warnings: list[str]
  ```
- **Algoritmo:**
  1. Validar que root existe y es directorio
  2. `os.walk(root, topdown=True)` para recorrer
  3. Podar directorios:
     - Skip: dot dirs, symlinks, IGNORE_DIRS predefinidos (`__pycache__`, `node_modules`, `.git`, `venv`, etc.)
     - Skip: dirs en `ignore_dirs` (por nombre o por path relativo)
  4. Por cada archivo:
     - Skip: dot files, symlinks, archivos vacios
     - Aplicar filtros include/exclude (glob patterns, comma-separated)
     - Clasificar: extension en supported_extensions → processable, sino → unsupported
  5. Si strict=True y hay unsupported: raise error
  6. Ordenar resultados
- **Logica de include/exclude:**
  - include: comma-separated globs (ej: `"*.pdf,*.md"`), si vacio incluye todo
  - exclude: comma-separated, pattern con `/` final es path prefix (ej: `"drafts/"`), sin `/` es glob de nombre (ej: `"*.tmp"`)
- **Deps:** `os`, `pathlib`, `fnmatch`, `dataclasses`
- **Error type:** FileNotFoundError, NotADirectoryError
- **Tests:** directorio con mezcla de archivos, directorio con dot files, directorio con subdirs ignorados, filtros include/exclude, modo strict

### 2. Constantes IGNORE_DIRS sugeridas

```python
IGNORE_DIRS = {
    "__pycache__", "node_modules", ".git", ".svn", ".hg",
    "venv", ".venv", "env", ".env", ".tox", ".nox",
    ".mypy_cache", ".pytest_cache", ".ruff_cache",
    "dist", "build", ".next", ".nuxt",
    "target",  # Rust/Java
    "vendor",  # Go
}
```
