rules.nvim · Debug & inspect · vimdoc

:help rules

A rule/checklist engine for Neovim

doc/rules.txt — rendered from the plugin's own vimdoc

*rules.txt*    A rule/checklist engine for Neovim

CONTENTS *rules-contents*

    Introduction ....... |rules-introduction|
    Setup .............. |rules-setup|
    Commands ........... |rules-commands|
    Gates .............. |rules-gates|
    Waivers ............ |rules-waivers|
    Ruleset format ...... |rules-ruleset-format|

INTRODUCTION *rules-introduction*

rules.nvim reads a set of rules you point it at, runs whichever of them can
be checked mechanically against a repo, and reports the rest as a worklist.
It ships with no bundled rules of its own — see the README's "Why no bundled
rules".

SETUP *rules-setup*

    require("rules").setup({
      rulesets = { "~/path/to/your/checklists" },
    })
rulesets is a list of files or directories; every .md file reachable from
each entry is scanned for fenced ```rule blocks. Each entry is expanded
(~, $VAR/${VAR}, %VAR%) before it is checked, so the ~/... form
above works as written; a path that still resolves to neither a directory
nor a readable .md file is a load error, notified rather than silently
loading zero rules.

lua_predicates (boolean, default true) decides whether lua_predicate
checks may run. A rule block body is always evaluated in an empty environment,
but a predicate is arbitrary Lua that runs later; set lua_predicates = false
to run a ruleset you did not write, and each predicate then reports error
("predicate not trusted") instead of running. See docs/RULESET-FORMAT.md.

COMMANDS *rules-commands*

                                                                  *:Rules_check*
:Rules check [{path}]
    Runs every rule whose ID prefix matches the required |--family| flag
    against {path} (default: the current working directory). Findings go to
    the quickfix list; a readable report opens in a tab, reusing a still-
    open report window instead of piling up a new one on every run. With
    --format=json, prints JSON instead and skips the quickfix list/buffer;
    never quits Neovim, so it is safe to run interactively. For a headless
    CI run with a real exit code, call
    require("rules").check_family_json(family, path) directly — see
    docs/BINDINGS.md in the plugin's repository for the recipe.

    --family={PREFIX}     required, e.g. --family=DEP
    --format={FORMAT}     optional, only "json" is recognized

                                                                   *:Rules_gate*
:Rules gate {name} [{path}]
    Runs every family configured for gate {name} (see |rules-gates|) as one
    combined report. --diff={ref} narrows findings to files changed since
    that git ref (tracked + untracked); a rule left with no findings inside
    the diff reports "pass". --format=json behaves like :Rules check's.

    --diff={REF}          optional, e.g. --diff=main
    --format={FORMAT}     optional, only "json" is recognized

                                                                   *:Rules_show*
:Rules show {id}
    Jumps straight to one rule's source location by its exact id (e.g.
    :Rules show DEP-06), without running a family check. Errors, rather
    than silently doing nothing, if no loaded rule has that id.

                                                                  *:Rules_stats*
:Rules stats
    A structural overview of every loaded rule: total count, and per-family
    totals split into automated (has a check) vs. manual, plus a severity
    breakdown. No check runs — this is catalog metadata, not a pass/fail
    report; opens in a readable buffer, or prints JSON with --format=json.

    --format={FORMAT}     optional, only "json" is recognized

GATES *rules-gates*

A gate is a named bundle of rule families, run together as one report — the
one place in this plugin that checks more than one family at once. No gates
are bundled; define them in setup():
    require("rules").setup({
      gates = {
        release = { "REL" },
        review = { "ERR", "LUA", "PRIN" },
      },
    })
See docs/BINDINGS.md in the plugin's repository for the --diff scoping
semantics in detail. A configured family that matches zero loaded rules
(a typo, or a family not yet migrated to fenced rule blocks) warns rather
than silently running a smaller gate than configured.

WAIVERS *rules-waivers*

A .rules-waivers.json at the checked root records consciously accepted
findings so a re-run doesn't keep flagging them:
    { "DEP-04": "legacy call site, ticket JIRA-123 tracks the actual fix" }
A waived rule that would otherwise fail or error reports as "waived"
instead of "fail" — visible in the buffer report with its reason, excluded
from the quickfix worklist, and never counted by check_family_json's exit
code. See docs/BINDINGS.md in the plugin's repository for detail.

:checkhealth rules cross-checks the current working directory's waivers
against every loaded rule and warns about an orphaned entry — a waiver
whose rule id was retired or mistyped, which would otherwise sit silently
protecting nothing.

RULESET FORMAT *rules-ruleset-format*

See docs/RULESET-FORMAT.md in the plugin's repository for the full format —
this is the reference for a Lua project's own :h request, not a duplicate
of that document's detail.

A rule is a Markdown section with a fenced ```rule code block:
    ### `DEP-06` — `vim.tbl_flatten()` is deprecated

    ```rule
    id = "DEP-06",
    severity = "recommended",
    check = { type = "grep", pattern = "vim%.tbl_flatten%(" },
    ```
check is optional. A rule without one is never given an automatic
pass/fail — it appears as a worklist entry instead.