> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reasonos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Lint SDK

> Language-agnostic linting framework with SDK-first extensibility — use any linter with any language

# Lint SDK

The Lint SDK provides a language-agnostic linting framework for RBS. Register linters for any language — Black, ESLint, Prettier, Ruff, Biome, or your own custom linter — and run them as part of your build pipeline. Linting failures cause builds to exit with code 1, making the SDK ideal for CI/CD enforcement.

<Tip>Linters are **not pre-installed**. RBS downloads and manages them as external dependencies, following the hermetic build principle. No system-level tool installation required.</Tip>

## Quick start

### Python projects

```python theme={null}
# WORKSPACE.rbs
load("@rbs//python/toolchain.rbs", "python_toolchain")
load("@rbs//python/lint.rbs", "register_python_linters")

# Auto-downloads and registers Python toolchain
python_toolchain(name = "python3", version = "3.12")

# Register linters (downloads Black and isort automatically)
register_python_linters(
    black_version = "24.1.1",
    isort_version = "5.13.2",
)
```

```python theme={null}
# BUILD.rbs
load("@rbs//python/lint.rbs", "py_lint")

py_lint(
    name = "lint",
    srcs = glob(["**/*.py"], exclude = [".rbs/**", "venv/**"]),
)
```

```bash theme={null}
rbs build //:lint
```

### Node.js / TypeScript projects

```python theme={null}
# WORKSPACE.rbs
load("@rbs//nodejs/toolchain.rbs", "nodejs_toolchain")
load("@rbs//nodejs/lint.rbs", "register_nodejs_linters")

# Auto-downloads and registers Node.js toolchain
nodejs_toolchain(name = "nodejs", version = "20.11.0")

# Register linters (downloads Prettier automatically)
register_nodejs_linters(
    prettier_version = "3.2.0",
    eslint_version = None,  # Skip ESLint if not needed
)
```

```python theme={null}
# BUILD.rbs
load("@rbs//nodejs/lint.rbs", "nodejs_lint")

nodejs_lint(
    name = "lint",
    srcs = glob(["src/**/*.ts"], exclude = ["node_modules/**", ".rbs/**"]),
    linters = ["prettier"],
)
```

```bash theme={null}
rbs build //:lint
```

***

## CI/CD integration

The Lint SDK causes builds to **exit 1** when linting fails, making it a natural fit for CI pipelines:

```python theme={null}
# BUILD.rbs
py_lint(
    name = "lint",
    srcs = glob(["**/*.py"], exclude = [".rbs/**"]),
)
```

```bash theme={null}
# In CI — exits with code 1 if any file fails linting
rbs build //:lint
```

***

## Pre-defined lint rules

### Python: `py_lint`

Runs Black and isort on Python files:

```python theme={null}
load("@rbs//python/lint.rbs", "py_lint")

py_lint(
    name = "lint",
    srcs = glob(["**/*.py"], exclude = [".rbs/**", "venv/**", "__pycache__/**"]),
)
```

### Node.js: `nodejs_lint`

Runs Prettier (and optionally ESLint) on JavaScript/TypeScript files:

```python theme={null}
load("@rbs//nodejs/lint.rbs", "nodejs_lint")

nodejs_lint(
    name = "lint",
    srcs = glob(["src/**/*.ts"], exclude = ["node_modules/**", "dist/**", ".rbs/**"]),
    linters = ["prettier"],  # Or ["prettier", "eslint"]
)
```

***

## Pre-defined linters

### Python linters

Registered via `register_python_linters()`:

| Linter     | Purpose        | Check mode                   | Fix mode         |
| ---------- | -------------- | ---------------------------- | ---------------- |
| **Black**  | Code formatter | `--check --diff`             | Formats in-place |
| **isort**  | Import sorter  | `--check-only --diff`        | Sorts in-place   |
| **flake8** | Style checker  | `--show-source --statistics` | No auto-fix      |
| **Ruff**   | Fast linter    | `check --show-source`        | `check --fix`    |

### Node.js / TypeScript linters

Registered via `register_nodejs_linters()`:

| Linter       | Purpose                      | Check mode                      | Fix mode        |
| ------------ | ---------------------------- | ------------------------------- | --------------- |
| **Prettier** | Code formatter               | `--check`                       | `--write`       |
| **ESLint**   | JavaScript/TypeScript linter | `--format stylish`              | `--fix`         |
| **tsc**      | TypeScript type checker      | `--noEmit --pretty`             | No auto-fix     |
| **Biome**    | Fast all-in-one linter       | `check --diagnostic-level=warn` | `check --apply` |

***

## Defining custom linters

Register any linter without modifying the build system — just define it in your configuration:

```python theme={null}
native.define_linter(
    name = "my_linter",
    language = "python",
    executable = "@python://my-lint:1.0.0:my-lint",
    check_args = ["--check", "--verbose"],
    fix_args = ["--fix"],
    file_patterns = ["*.py"],
    exclude_patterns = ["*_test.py", "__pycache__/*"],
    config_file = ".my-lintrc",
    environment = {
        "PYTHONPATH": "/custom/path",
    },
    success_exit_codes = [0, 1],
)
```

### Configuration options

| Option               | Type   | Description                                                  |
| -------------------- | ------ | ------------------------------------------------------------ |
| `name`               | string | Unique identifier for the linter.                            |
| `language`           | string | Target language (e.g., `"python"`, `"nodejs"`, `"go"`).      |
| `executable`         | string | Linter binary path, name, or protocol reference (see below). |
| `check_args`         | list   | Arguments for lint checking mode.                            |
| `fix_args`           | list   | Arguments for auto-fix mode.                                 |
| `file_patterns`      | list   | Glob patterns for files to lint (default: `["*"]`).          |
| `exclude_patterns`   | list   | Glob patterns for files to exclude.                          |
| `config_file`        | string | Optional path to a linter configuration file.                |
| `environment`        | dict   | Environment variables to set when running the linter.        |
| `success_exit_codes` | list   | Exit codes that indicate success (default: `[0]`).           |

### Executable protocol format

Reference linter binaries from managed external dependencies using the protocol format:

```
@{ecosystem}://{package}:{version}:{binary}
```

Examples:

* `@python://black:24.1.1:black` — Python's Black formatter
* `@nodejs://prettier:3.2.0:prettier` — Prettier
* `@nodejs://typescript:5.3.3:tsc` — TypeScript compiler

RBS automatically locates the binary in the managed dependency cache, resolving the correct platform and version.

***

## `ctx.lint` API reference

The `ctx.lint` module is available inside any rule implementation.

### `ctx.lint.run()`

Run a linter in check mode:

```python theme={null}
result = ctx.lint.run(
    linter = "black",
    files = ["main.py", "utils.py"],
    working_dir = ".",
    extra_args = ["--verbose"],
)

# result.success  → bool (whether linting passed)
# result.output   → string (stdout/stderr from linter)
# result.exit_code → int
# result.files_linted → int
```

### `ctx.lint.fix()`

Run a linter in auto-fix mode:

```python theme={null}
result = ctx.lint.fix(
    linter = "prettier",
    files = ["app.ts", "utils.ts"],
)
```

### `ctx.lint.run_all()`

Run all registered linters for a language:

```python theme={null}
result = ctx.lint.run_all(
    language = "python",
    files = ["main.py", "lib.py"],
)

# result.success → True if ALL linters passed
# result.results → list of individual linter results
```

### `ctx.lint.fix_all()`

Run all linters for a language in fix mode:

```python theme={null}
result = ctx.lint.fix_all(
    language = "nodejs",
    files = glob(["**/*.ts"]),
)
```

### `ctx.lint.list()`

List all registered linters:

```python theme={null}
# List all linters
all_linters = ctx.lint.list()

# List only Python linters
python_linters = ctx.lint.list(language = "python")
```

### `ctx.lint.get_linter()`

Get a specific linter's configuration:

```python theme={null}
linter = ctx.lint.get_linter(name = "black")
if linter:
    print("Executable:", linter.executable)
    print("Check args:", linter.check_args)
```

***

## Complete examples

### Django project

```python theme={null}
# WORKSPACE.rbs
load("@rbs//python/toolchain.rbs", "python_toolchain")
load("@rbs//python/lint.rbs", "register_python_linters")
load("@rbs//python/dependencies.rbs", "py_repository")

python_toolchain(name = "python3", version = "3.12")

register_python_linters(
    black_version = "24.1.1",
    isort_version = "5.13.2",
)

py_repository(name = "django_repo", package = "django", version = "5.0.1")
```

```python theme={null}
# BUILD.rbs
load("@rbs//python/rules.rbs", "py_binary")
load("@rbs//python/lint.rbs", "py_lint")

py_binary(
    name = "server",
    srcs = glob(["**/*.py"]),
    main = "manage.py",
    deps = [":django_repo"],
)

py_lint(
    name = "lint",
    srcs = glob(["**/*.py"], exclude = [".rbs/**", "venv/**", "__pycache__/**"]),
)
```

### TypeScript project

```python theme={null}
# WORKSPACE.rbs
load("@rbs//nodejs/toolchain.rbs", "nodejs_toolchain")
load("@rbs//nodejs/lint.rbs", "register_nodejs_linters")
load("@rbs//nodejs/dependencies.rbs", "nodejs_repository")

nodejs_toolchain(name = "nodejs", version = "20.11.0")

register_nodejs_linters(prettier_version = "3.2.0")

nodejs_repository(name = "express_repo", package = "express", version = "4.18.2")
nodejs_repository(name = "typescript_repo", package = "typescript", version = "5.3.3", host = True)
```

```python theme={null}
# BUILD.rbs
load("@rbs//nodejs/rules.rbs", "nodejs_binary")
load("@rbs//nodejs/lint.rbs", "nodejs_lint")

nodejs_binary(
    name = "server",
    srcs = glob(["src/**/*.ts"]),
    main = "src/server.ts",
    deps = [":express_repo", ":typescript_repo"],
)

nodejs_lint(
    name = "lint",
    srcs = glob(["src/**/*.ts"], exclude = ["node_modules/**", "dist/**", ".rbs/**"]),
)
```

***

## Adding support for a new ecosystem

The SDK-first design means you can add linter support for any language without modifying the build system:

```python theme={null}
# Register a new linter for any language
native.define_linter(
    name = "golangci-lint",
    language = "go",
    executable = "golangci-lint",
    check_args = ["run"],
    fix_args = ["run", "--fix"],
    file_patterns = ["*.go"],
    success_exit_codes = [0],
)
```

***

## Troubleshooting

### Linter not found

If you see "executable file not found":

1. Check that the external dependency is declared in `WORKSPACE.rbs`.
2. Verify the linter is registered with `register_*_linters()`.
3. Confirm the package was downloaded (check `.rbs/external-deps/`).

### Wrong files being linted

Use `glob()` with `exclude` patterns to skip generated files and dependencies:

```python theme={null}
srcs = glob(
    ["**/*.py"],
    exclude = [".rbs/**", "venv/**", ".venv/**", "__pycache__/**"],
)
```
