# Library usage (`jaxlint.core`)

Import from **`jaxlint.core`** for the stable façade (no JAX dependency):

```python
from pathlib import Path

from jaxlint.core import (
    Diagnostic,
    Severity,
    describe_rules,
    diagnostics_for_python_source,
    format_compact,
    format_diagnostics,
    format_github_annotations_json,
    format_json,
    format_sarif,
    format_text,
    resolve_paths,
    run_checks,
)
from jaxlint.core.config import LintConfig, load_config
```

## Load configuration

```python
cfg = load_config(Path("/your/project"))  # walks upward for jaxlint.toml / pyproject.toml
# or
cfg = LintConfig.default()
cfg = LintConfig(select=frozenset(["JL"]), strict_shapes=True)
```

## Run checks

```python
paths = [Path("src/myproject")]
diags = run_checks(paths, cfg=cfg, perf=True, doc=True, mathjx=True)
# Optional: jobs=None (default parallel budget) or jobs=1 (serial), jobs>1 (thread pool).
# Optional: cache_dir=Path(".jaxlint-cache") — on-disk cache of per-file diagnostics (see configuration.md).
```

- **`run_checks` return order:** The list is **pre-sorted** by `Diagnostic.sort_key()` (`path`, `line`, `column`, `rule_id`). Callers should not rely on discovery or insertion order.
- **`jobs`:** `None` uses `min(32, usable CPU count)` (same semantics as the CLI: `os.process_cpu_count()` on Python 3.13+, else `os.cpu_count()`); `1` forces serial file processing; larger values use a `ThreadPoolExecutor` over files. `LintConfig` must be treated as **read-only** across workers.
- **`perf`:** AST performance sensors (JL rules).
- **`doc`:** Docstring sections (JD rules, plus style integration).
- **`mathjx`:** MathJax diagnostics (JM rules) layered on docstring text.
- **`config_start`:** Optional anchor for `load_config` when `cfg` is omitted.

## In-memory diagnostics (`diagnostics_for_python_source`)

For a **path** plus **source text** (for example an unsaved editor buffer), **`diagnostics_for_python_source`** returns the same **`list[Diagnostic]`** as **`run_checks`** would for that content: **sorted** by `Diagnostic.sort_key()`, and **no on-disk cache** — useful for tests, custom runners, and the optional **[language server](lsp.md)**.

```python
text = Path("example.py").read_text(encoding="utf-8")
cfg = load_config(Path("example.py").parent)
diags = diagnostics_for_python_source(Path("example.py"), text, cfg=cfg)
```

Optional flags match **`run_checks`**: **`perf`**, **`doc`**, **`mathjx`** (defaults mirror **`jaxlint check`**).

Filter by severity in Python:

```python
errors = [d for d in diags if d.severity == Severity.ERROR]
```

## Format results

```python
print(format_text(diags))
print(format_json(diags))
print(format_github_annotations_json(diags))
print(format_diagnostics(diags, "github"))  # same as format_github_annotations_json
print(format_diagnostics(diags, "sarif", cfg=cfg))  # cfg carries sarif-rule-docs-base from load_config
print(format_diagnostics(diags, "compact"))
```

`format_*` helpers sort again by the same key for display, so output stays deterministic even if you build a list manually.

`format_diagnostics(diags, fmt, cfg=None)` accepts `text`, `compact`, `json`, `github` (alias: `github-annotations`), `sarif` (alias: `sarif-2.1.0`) and raises `ValueError` on unknown `fmt`. For **`sarif`**, pass **`cfg`** when you need **`helpUri`** from **`sarif-rule-docs-base`** (same object you pass to **`run_checks`** or from **`load_config`**). **`format_sarif(diags, tool_version=..., cfg=...)`** uses **`cfg`** the same way.

## Rule catalogue

- `RULES` and `describe_rules()` expose the installed rule ids (aligned with `jaxlint rules` CLI).
- Human-oriented table: [rules.md](rules.md).

## Docstring rubric

The CLI uses `RubricScorer` from **`jaxlint.core.doc.rubric_impl`** for `jaxlint score`. Import that class directly if you need the same normalization outside the CLI.

## Optional HLO

See [hlo.md](hlo.md). Do **not** import `jaxlint.hlo` in code paths that must stay JAX-free.
