# AGENTS.md

This file guides agentic coding assistants working on the Vibelexity codebase.

## Development Commands

```bash
# Run all tests
uv run pytest

# Run tests for a single metric/language
uv run pytest tests/test_cognitive/test_python.py

# Run a specific test
uv run pytest tests/test_cognitive/test_python.py::test_simple_function -v

# Run tests matching a pattern
uv run pytest -k "test_simple"

# Install/sync dependencies
uv sync

# Run the analyzer
uv run vibelexity <directory>
python main.py <directory>
```

## Code Style Guidelines

### Imports
- Start every Python file with `from __future__ import annotations`
- Standard library imports first, then third-party, then local modules
- Group imports logically with blank lines between groups

### Type Annotations
- Use `name: type` syntax for all function parameters and returns
- Use PEP 604 union types: `str | None` instead of `Optional[str]`
- Use generic type syntax: `list[str]`, `dict[str, int]`, `tuple[str, FunctionComplexity]`
- Full type hints are required for all public functions

### Naming Conventions
- Functions: snake_case (`compute_halstead`, `analyze_source`)
- Variables: snake_case (`func_node`, `total`, `nesting`)
- Private functions: prefix with underscore (`_walk`, `_flatten_boolean_ops`)
- Constants: UPPER_SNAKE_CASE (`PY_LANGUAGE`, `_PY_NESTING_INCREMENTORS`)
- Classes: PascalCase (`FileComplexity`, `HalsteadMetrics`)

### Data Models
- Use `@dataclass` for all data types
- Optional fields should default to `None` or `0`
- Use `field(default_factory=list)` for mutable defaults
- All models are in `models.py`; import them from there

### Code Organization
- **Three-layer architecture**: parsers → metrics → analyzers
- **Parsers**: `parsers/<lang>.py` - parse AST and collect function nodes
- **Metrics**: `metrics/<metric>/<lang>.py` - compute single metric from AST nodes
- **Analyzers**: `analyzers/<lang>.py` - orchestrate all computations into `FileComplexity`
- Each metric module exposes `compute_<metric>_<lang>()` function
- Each analyzer exposes `analyze_source()` and `analyze_file()` functions

### Error Handling
- Guard against invalid state with early returns: `if n == 0 or n2 == 0: return HalsteadMetrics(...)`
- Use return values rather than exceptions for metric computation
- Avoid raising exceptions in metric code; return safe defaults

### Docstrings
- Keep docstrings brief and descriptive
- One docstring per function/class/module
- Explain purpose and key details, avoid implementation trivia

### Testing Style
- Create helper functions in test files: `_score()`, `_scores()` patterns
- Use descriptive test names: `test_nested_if`, `test_chained_same_op`
- Comments in code snippets show expected complexity: `# +1`, `# +1 +1 (nesting 1)`
- Test both happy paths and edge cases (empty inputs, division by zero guards)

### AST Traversal Patterns
- Recursive traversal is standard: `_walk(node, nesting: int, ...) -> int`
- Use `node.children` to iterate child nodes
- Check `node.type` to identify AST node types
- Use `node.start_point[0] + 1` for line numbers (0-indexed → 1-indexed)
- Use `node.text.decode()` to get string content from AST nodes

### Shared Code Patterns
- Use factory functions from `analyzers/shared.py` to reduce duplication
- Use common patterns from `common.py` for utilities (Halstead, MI, LDI, boolean scoring)
- Language-specific `create_*` factories in `parsers/shared.py`, `metrics/cognitive/common.py`