Skip to content

Concepts

The Concepts section is the conceptual foundation that the rest of the docs assume. Skim it once; come back when something downstream confuses you.

Everything here is one idea seen from six angles. You declare rules (fact-gated, and composed from other configs); alint targets each rule at a set of files, evaluates them (per file and across files), and emits a report whose exit code gates CI; then fix, baseline, and an agent surface carry it into a real repo. This map is the whole section in one picture, and every box below is a page in the sidebar:

The alint mental model: a rule record, and the pipeline it drives A rule record (id, kind, level, paths, when, fix, message) takes facts and extends as inputs, then drives a five-stage pipeline: target, evaluate, report with an exit code, adopt via fix and baseline, and an agent surface. Each stage maps to a Concepts group. anatomy of a rule inputs feed the rule COMPOSITION facts: has_docs extends: rust@v1 rule the rule model · START HERE id: readme-existsstable handle kind: file_existswhich check runs level: error→ exit code paths: [README.md]which files when: facts.has_docsfact gate fix: file_createauto-repair message · policy_urlshown on failure the flow it drives six ideas, one pipeline TARGETINGtarget: the walk · globs · when: · changed BEYOND SINGLE FILESevaluate: cross-file + structured queries START HERE · SEVERITYreport: error · warning · info → exit code ADOPTIONadopt: fix repairs · baseline grandfathers AGENTSagents: rules → instruction + fix_command one config in, one gated report out

alint is a static Rust binary that lints the shape of a repository. Where ESLint lints code and Semgrep lints semantics, alint lints the things in between: required files (READMEs, LICENSEs, SECURITY.md), filename conventions, content patterns, the values inside package.json / Cargo.toml / GitHub workflows, and cross-file relationships like “every package has a README” or “every header has a matching source file.”

The section is six groups, each a stage of the mental model above. Read it start to finish for the whole picture, or jump to the group that owns your question:

The rest of this page is a quick tour of the load-bearing ideas; each links into the group that covers it in depth.

Every rule has:

  • id: a stable, kebab-case identifier. Required. Used to override or disable the rule from a child config.
  • kind: which built-in rule implementation to invoke (e.g. file_exists, json_path_equals, for_each_dir). Required.
  • level: error, warning, info, or off. Required.
  • paths: a glob, list of globs, or {include, exclude} pair selecting which files the rule applies to. Required for most kinds.
  • when: an expression gating the rule on facts or vars. Optional.
  • fix: a fix-op declaration that turns this rule into an auto-fixable one. Optional.
  • message / policy_url: display fields shown when the rule fires. Optional.

Plus kind-specific options. For example, file_min_lines takes min_lines: <int>; json_path_matches takes path (a JSONPath) and matches (a regex).

alint configs compose via extends:. A config can inherit from local files, HTTPS URLs (with SRI hashes), or alint://bundled/<name>@<rev>. Children override inherited rules field-by-field by id. You only declare the fields that change. only: / except: filters narrow the inherited rule set further. nested_configs: true opts in to discovering .alint.yml files in subdirectories and auto-scoping their rules to the subtree they live in.

extends: is also a trust boundary: a fetched or bundled ruleset can tighten your checks but is barred from declaring process-spawning rules, custom: facts, allow_out_of_root, or a baseline, so adopting someone’s ruleset can never make your machine run their code.

See the Cookbook for composition patterns in practice.

Facts evaluate properties of the repo once per run and surface them as named values:

facts:
- id: has_rust
any_file_exists: [Cargo.toml]

Rules reference facts in when: to gate themselves conditionally:

- id: rust-snake-case
when: facts.has_rust
kind: filename_case
paths: "src/**/*.rs"
case: snake
level: error

The when: grammar supports boolean logic (and / or / not), comparison (== != < <= > >=), in (list / substring), matches (regex), literal types, and facts.X / vars.X / iter.X / env.X identifiers. It’s deliberately bounded: no arbitrary code, no dynamic evaluation.

Rules that opt in declare a fix: block. Twelve ops cover content edits (trim whitespace, append newline, normalize line endings, strip BOM / bidi / zero-width, collapse blank lines) and path-level changes (create / remove / rename / prepend / append).

- id: trim-trailing-whitespace
kind: no_trailing_whitespace
paths: "**/*.md"
level: info
fix:
file_trim_trailing_whitespace: {}

Preview with alint fix --dry-run; apply with alint fix. Content-editing ops honour fix_size_limit (default 1 MiB) and skip oversize files rather than rewriting them.

Eight formats: human (default; colorized; grouped by file), json (stable schema), sarif (SARIF 2.1.0 for GitHub Code Scanning), github (::error:: / ::warning:: workflow commands for inline PR annotations), markdown (PR-comment tables), junit (CI test-report shape), gitlab (Code Quality), agent (LLM-shaped JSON with per-violation agent_instruction).

Terminal window
alint check --format json --compact

The --compact flag flips human output to one line per violation, suitable for piping to editors / grep / wc -l.