# CLAUDE.md

**PySentry** - A fast, reliable security vulnerability scanner for Python projects, written in Rust.

## Codebase Map

**Architecture:** Dual interface (Rust binary `pysentry` + Python package `pysentry-rs`)

**Key Modules:**
- `src/main.rs` - CLI entry point
- `src/lib.rs` - `AuditEngine` API (high-level audit orchestrator)
- `src/cache/` - Multi-tier caching
  - `audit.rs` - `AuditCache` interface
  - `storage.rs` - `CacheEntry` abstraction
- `src/dependency/` - Dependency scanning + external resolvers
  - `scanner.rs` - `DependencyScanner` (main orchestration)
  - `resolvers/uv.rs` - UV resolver (preferred, Rust-based)
  - `resolvers/pip_tools.rs` - pip-compile fallback
- `src/parsers/` - 7 parsers with priority system
  - Priority 1: lock.rs (uv.lock), poetry_lock.rs, pipfile_lock.rs, pylock.rs
  - Priority 3: pyproject.rs (requires external resolver)
  - Priority 4: pipfile.rs (requires external resolver)
  - Priority 5: requirements.rs (requires external resolver)
  - See `ParserTrait::priority()` for implementation details
- `src/providers/` - PyPA, PyPI, OSV.dev integrations
- `src/vulnerability/` - Matching engine + database
- `src/output/` - Human, JSON, SARIF, markdown reports
- `src/config.rs` - Hierarchical TOML config

**Cache Locations:** `~/.cache/pysentry/vulnerability-db/`, `~/.cache/pysentry/dependency-resolution/`

## Common Development Commands

### Building & Testing

```bash
# Build release binary
cargo build --release

# Run all tests (embedded #[cfg(test)] modules)
cargo test

# Run tests with output
cargo test -- --nocapture

# Run specific test
cargo test test_name

# Build Python bindings (requires maturin)
maturin develop

# Build Python wheel
maturin build --release
```

### Code Quality

```bash
# Format code
cargo fmt --all

# Check formatting (CI)
cargo fmt --all -- --check

# Lint with Clippy
cargo clippy --all-targets --all-features

# Clippy with warnings as errors (CI)
cargo clippy --all-targets --all-features -- -D warnings

# Type checking
cargo check --all-targets --all-features
```

### Development Tools

```bash
# Security audit
cargo audit

# Run benchmarks
cd benchmarks && python main.py

# Pre-commit hooks
pre-commit run --all-files

# GitHub CLI (READ-ONLY)
gh pr view <number>
gh issue view <number>
```

## Critical Development Rules

**Resolver Isolation (CRITICAL):**
- Resolvers MUST run in isolated temp directories
- ALWAYS force cache to temp dir (`UV_CACHE_DIR`, `PIP_CACHE_DIR`) - never use project cache
- See `UvResolver::create_isolated_temp_dir()` and `PipToolsResolver::create_isolated_temp_dir()` for reference
- Default timeout: 5 minutes (hardcoded) - may fail on slow networks/large dependency trees

**Error Handling:**
- Use `?` operator, NEVER `.unwrap()` or `.expect()` in production code
- Replace `.unwrap()` with `.ok_or_else()` or `.context()` for proper error propagation
- Exception: `let _ = self.cached_*.set()` is intentional (OnceLock set failure is benign)
- All errors should propagate with context or be logged at WARN level
- Never `let _ =` on fallible operations without logging at WARN level

**Parser Priority System:**
- Lower number = higher priority (1-5 scale)
- Lock files (priority 1) always preferred over manifest files (priority 3-5)
- When implementing new parsers, return priority via `ParserTrait::priority()`

**Parser Limitations to Remember:**
- Path dependencies NOT extracted from lock files (virtual/editable installs skipped)
- Poetry 2.x uses different marker format than 1.x (custom deserializer handles this)
- Virtual packages and editable installs are excluded from vulnerability scans

**Cache Safety:**
- Atomic writes via temp-file + rename pattern (see `CacheEntry::write_atomic_sync`)
- Idempotent deletes - `NotFound` errors treated as success
- Write failures only logged at WARN level, never retried
- Resolution cache: 24h TTL, content-based keys (requirements + resolver + Python version)
- Vuln DB cache: 24h TTL

**CLI consistency:**
- When changing CLI keep it consistent with Python src/python.rs

## Rust Guidelines (PySentry-Specific)

**Code Structure:**
- Prefer `module_name.rs` over `mod.rs` (Rust 2024 / Zed guideline)
- Only "why" comments, NO organizational/summary comments
- Full words for variable names (avoid single-letter except standard idioms like `i` for index)

**Safety:**
- Prefer `.get()` over `[]` indexing to avoid panic-on-bounds
- Use `Option` and `Result` extensively, never bypass with unwrap
- All public APIs should return `Result<T>` for fallible operations

## Config & Runtime

**Config Discovery:** `.pysentry.toml` (project) → `~/.config/pysentry/config.toml` (user) → `/etc/pysentry/config.toml` (system)
**Override Env Vars:** `PYSENTRY_CONFIG` (path override), `PYSENTRY_NO_CONFIG` (disable all config)

**CLI Structure:** See src/cli.rs and src/main.rs
- Main: `pysentry [options] [path]`
- Subcommands: `resolvers`, `check-version`, `config {init|show|validate}`
