# CLAUDE.md

## Project Overview

**fuzzy-json** (PyPI: `fuzy-jon`) is a lightweight Python package that repairs invalid JSON structures, particularly those generated by LLMs like OpenAI's function calling feature. It uses a pushdown automata parser to fix common JSON syntax errors without retries or prompt engineering.

- **Repository:** https://github.com/livingbio/fuzzy-json
- **License:** MIT
- **Python:** >= 3.10 (tested on 3.10, 3.11, 3.12, 3.13)
- **Runtime dependency:** `json5` only

## Project Structure

```
src/fuzzy_json/
├── __init__.py          # Public API: exports `loads`
├── decoder.py           # Core implementation (pushdown automata parser)
├── py.typed             # PEP 561 type hints marker
└── tests/
    ├── test_decoder.py  # Test suite (pytest + syrupy snapshots)
    ├── test_data/
    │   ├── valid/       # Valid JSON test files (*.json)
    │   └── invalid/     # Invalid JSON test files (*.jsonx)
    └── __snapshots__/   # Snapshot assertion files
```

## Common Commands

```bash
# Install dependencies (uses uv)
uv sync --extra dev

# Run tests with coverage
uv run pytest ./src --cov=./src --record-mode=none --cov-report xml

# Run all linting and formatting checks
uv run pre-commit run --all-files

# Individual tools
uv run ruff check src/          # Lint
uv run ruff format src/         # Format
uv run mypy src/                # Type check
```

## Architecture

The core is a **pushdown automata** JSON parser in `decoder.py`. Each parser state is a function decorated with `@state` that takes `(input: str, stack: list[str])` and returns the remaining valid JSON string or raises `ValueError`.

**Key states:** `state_start` → `state_root_object` → `state_object`/`state_value` → `state_post_value` → `state_finish`

**Public API:**
- `loads(json_str: str, auto_repair: bool = True) -> Any` — Main entry point. Tries standard parsing first, then repairs if needed.
- `repair_json(json_str: str) -> str` — Internal: repairs invalid JSON syntax and returns a valid JSON string.

**Recovery strategies built into the parser:**
- Missing closing braces/brackets
- Trailing commas
- Unescaped quotes in strings
- Missing escape characters
- Mismatched brackets/braces

## Code Conventions

- **Formatting:** Ruff with 240 char line length, double quotes, space indentation
- **Lint rules:** E, W, F, I, B, C4, UP (pycodestyle, pyflakes, isort, bugbear, comprehensions, pyupgrade)
- **Type hints:** Full annotations throughout; mypy enforced (Python 3.10 target)
- **Testing:** pytest with `syrupy` snapshot assertions. Test files use `JSONSnapshotExtension`.
- **Build system:** Hatchling (`pyproject.toml` only, no setup.py)

## Testing Conventions

- Valid JSON test cases go in `src/fuzzy_json/tests/test_data/valid/*.json`
- Invalid JSON test cases go in `src/fuzzy_json/tests/test_data/invalid/*.jsonx`
- Snapshot tests use `syrupy` — update snapshots with `--snapshot-update` flag
- Always run tests with `--record-mode=none` in CI

## CI/CD

- **GitHub Actions:** Tests run on push/PR across Python 3.10–3.13
- **Pre-commit hooks:** JSON/TOML/YAML validators, ruff, codespell, mypy
- **Publishing:** Automated PyPI publish on GitHub release via `pypa/gh-action-pypi-publish`
- **Coverage:** Uploaded to Codecov
