qredo

Native Elixir linting with Credo-compatible behavior, written in Rust.

qredo runs Credo checks without the BEAM: a rule-kernel library, a 7-stage pipeline runner (selection, scope, priority, suppression, exit status) and a qredo CLI that reads your project's .credo.exs and prints issues with OR-combined exit codes, like mix credo.

Status: early. 120 rule kernels exist and the admitted compatibility corpora pass; the CLI serves static configs with all 81 documented rule-specific parameters and all five general parameters validated by schema, including explicit nil values that select Credo defaults, and fails closed with an explicit reason on anything else. See ROADMAP.

Install with Mix

Add qredo from Hex as a development dependency:

defp deps do
[
{:qredo, "~> 0.1.0", only: [:dev, :test], runtime: false}
]
end

Then run it through Mix; the first invocation downloads and SHA-256 verifies the native executable for the current platform:

mix deps.get
mix qredo --strict

Set QREDO_BUILD=source or run mix qredo.install --source to compile with the pinned Rust toolchain instead. QREDO_BINARY_PATH selects an existing binary, and QREDO_OFFLINE=1 prevents downloads.

Install with Cargo

Requires Rust 1.91.1 (pinned in rust-toolchain.toml) and no Elixir installation for linting itself:

cargo install --path crates/qredo

Usage

qredo [PATH] [--config-file FILE] [--config-name NAME] [--strict] [--min-priority N] [--mute-exit-status] [--only CHECK,...] [--ignore CHECK,...] [--format text|json] [--stale]
qredo apps/my_app --strict
qredo --only IoInspect,Dbg --format json
qredo --stale # reuse cached results for unchanged files

Files come from the config's files.included (defaulting to lib/ and test/), minus files.excluded; build directories are never descended into. On an unsupported configuration qredo prints the reason and exits 2 instead of running partial analysis.

Incremental runs (--stale)

qredo suggest --stale (and list --stale) reuse cached results for unchanged files: per-file issues are keyed by content hash, consistency votes by per-file counts, and the global majority is recomputed from merged counts without re-parsing cached files. The cache lives under ~/.cache/qredo/ (or $XDG_CACHE_HOME/qredo), keyed by project root plus a fingerprint over tool version, config bytes, environment snapshot, check list, selection and minimum priority. Any mismatch — or a flipped consistency majority — fails open to a full run, so --stale output always matches a fresh run.

Library

use qredo::check_kernel;
let findings = check_kernel(
"Credo.Check.Readability.TrailingBlankLine",
"defmodule Example do\nend",
).unwrap();
assert_eq!(findings[0].line, 2);

Full-pipeline runs go through integration::execute, which serves supported configs and reports an explicit Fallback reason otherwise.

Development

scripts/check # rustfmt, Clippy (-D warnings), tests

Releases

Every push to main runs the complete quality gate, builds five native targets on two runners, and replaces the dev GitHub prerelease. Development asset names include the exact Git commit consumed by a Mix git dependency.

Publishing a non-prerelease GitHub release such as v0.1.0 verifies that its tag matches both VERSION and crates/qredo/Cargo.toml, uploads immutable native assets plus SHA256SUMS, marks it latest, and publishes the matching Mix package to Hex. The repository must define a HEX_API_KEY Actions secret with package publishing permission.

See AGENTS.md for the contribution workflow, ARCHITECTURE.md for the design and compatibility for provenance.

License

MIT. Compatibility corpora derived from Credo's test suite keep their upstream MIT license and attribution; see compatibility. The upstream directory additionally carries Credo's own LICENSE file.