rrubocop
10x faster RuboCop drop-in โ same CLI, .rubocop.yml, and offense output, without maintaining RuboCop gems. Linting and simple config need no Ruby; complex ERB still does. Point it at an existing project and use it like RuboCop.
One binary โ runs the same way regardless of rbenv, rvm, system Ruby, or Docker. Uses tree-sitter; no Ruby runtime required for linting or simple ERB. Unsupported ERB falls back to ruby / bundle exec ruby.
Drop-in parity: reads your existing .rubocop.yml, emits RuboCop-like text/JSON, supports -a/-A autocorrect, and grows cop coverage over time.
Baseline (shown in --help / --version): rubocop 1.91.0 with rubocop-rails 2.37.0, rubocop-performance 1.27.0, rubocop-rspec 3.9.0, rubocop-rspec_rails 2.32.0, rubocop-factory_bot 2.28.0 โ pinned in src/resources/baseline.json.
Parity runs have been verified over 600+ installed Ruby gem projects (RuboCop vs rrubocop on the same trees).
Install
brew install adrianov/rrubocop/rrubocop
macOS (.tar.gz from GitHub Releases; binary + man):
curl -LO https://github.com/adrianov/rrubocop/releases/download/v0.8.15/rrubocop-0.8.15-aarch64-apple-darwin.tar.gz
tar -xzf rrubocop-0.8.15-aarch64-apple-darwin.tar.gz
sudo cp rrubocop-0.8.15-aarch64-apple-darwin/rrubocop /usr/local/bin/
sudo mkdir -p /usr/local/share/man/man1
sudo cp rrubocop-0.8.15-aarch64-apple-darwin/rrubocop.1 /usr/local/share/man/man1/
Ubuntu / Debian (.deb from GitHub Releases; amd64, Ubuntu 22.04+ / Debian bookworm+):
curl -LO https://github.com/adrianov/rrubocop/releases/download/v0.8.15/rrubocop_0.8.15-1_amd64.deb
sudo dpkg -i rrubocop_0.8.15-1_amd64.deb
man rrubocop
rrubocop [OPTIONS] [PATH]...
Features
- Config โ RuboCop-compatible resolution: walk-up discovery,
inherit_from / inherit_gem / require/plugins, inherit_mode (merge/override), nested .rubocop.yml overrides, DisabledByDefault, NewCops / Enabled: pending, plus per-cop Enabled / Exclude / Include / options; public gem defaults/inherit_gem YAML are vendored by version (from Gemfile.lock, with same_as aliases when YAML is identical) and parsed in memory; private/unlisted gems fall back to bundle info --path; simple ERB in YAML expands natively (Ruby only for unsupported tags)
- Autocorrect โ
-a (safe) / -A (all)
- Output โ
progress (marks stream as files finish), text, json, github, quiet, files, โฆ (TTY color like RuboCop; --color / --no-color)
- Directives โ
# rubocop:disable / enable
- Parser โ tree-sitter-ruby (no Prism / no CRuby)
- Cache โ content-addressed
cache.redb under $RRUBOCOP_CACHE_DIR or $XDG_CACHE_HOME/rrubocop / ~/.cache/rrubocop (same style as abcop); --cache false skips reads but still writes
- Prefer MCP with LLMs.
rrubocop --mcp is the best way to use rrubocop from an agent: the model gets offenses as soon as it writes, so it can fix and autocorrect in the same turn and ship effective code right away โ not after a later CLI/CI pass. Official Rust rmcp SDK; same tools as RuboCop 1.85+: rubocop_inspection and rubocop_autocorrection
Reference implementations: nitrocop (architecture & fixtures), upstream RuboCop.
Built-in cops
Departments grow breadth-first from common RuboCop plugin sets (nitrocop / gem sources). Notable coverage:
| Department | Notes |
|---|
Layout/* | 90 layout cops (alignment, spacing, empty lines, indentation; all with autocorrect) |
Lint/*, Style/*, Naming/*, Security/* | 69 / 143 / 9 / 4 |
Metrics/* | AbcSize, BlockNesting, ClassLength, CollectionLiteralLength, ModuleLength, ParameterLists |
Rails/* | 62 rails cops |
Performance/* | 24 performance cops |
RSpec/* | 104 rspec cops |
RSpecRails/* | 2 (HttpStatus, InferredSpecType) |
FactoryBot/* | 4 (AssociationStyle, AttributeDefinedStatically, CreateList, FactoryClassName) |
Bundler/*, Gemspec/*, Rake/* | 4 / 4 / 5 |
GraphQL/* | 26 rubocop-graphql cops |
Use rrubocop --list-cops for the full registered set, and rrubocop --list-autocorrectable-cops for cops with -a/-A support (180 with autocorrect so far; Layout complete; Style/Lint/Rails/RSpec growing).
Usage
rrubocop
rrubocop app lib
rrubocop -f json .
rrubocop -a .
rrubocop -A .
rrubocop --only Metrics/AbcSize lib
rrubocop --rubocop-version 1.79.2 .
rrubocop --list-cops
rrubocop -L
rrubocop -F 10
rrubocop --mcp
Exit codes: 0 clean, 1 offenses at/above --fail-level, 2 error.
MCP (Model Context Protocol)
Preferred for LLM / agent workflows. Wire rrubocop as an MCP server so the model can call rubocop_inspection / rubocop_autocorrection while it edits: feedback arrives in the same turn, the agent corrects soon, and it writes clean code on the first pass instead of discovering offenses only in CI.
rrubocop --mcp runs a long-lived MCP server on stdio โ same idea as RuboCopโs MCP, with no Ruby mcp gem. Tools:
| Tool | Purpose |
|---|
rubocop_inspection | Lint via path / paths (string or array; required for filesystem inspect) and/or inline source_code; returns LSP-shaped offense JSON |
rubocop_autocorrection | Same targets as inspection; safety: true = safe only; writes files when a filesystem path is set |
Always pass path or paths (absolute file or directory preferred; path may be a string or an array). The server walks up from the first target to find .rubocop.yml. Omitting targets for filesystem ops errors out (avoids scanning $HOME when MCP cwd is mis-set). Files without a recognized Ruby extension, filename, or shebang (e.g. YAML/JSON configs) are silently skipped โ same as upstream MCP, whose TargetFinder runs in :only_recognized_file_types mode โ so passing mixed paths yields no Lint/Syntax noise; summary.target_file_count reflects inspected files only. A long-lived global MCP started from $HOME otherwise inherits ~/.rubocop.yml (AllCops: Enabled: false is common) and misses project cops such as RSpec/ExampleLength.
- MCP Registry name:
mcp-name: io.github.adrianov/rrubocop
Metadata lives in server.json (Cargo package on crates.io). Each v* tag publishes that listing to the MCP Registry.
Example client config (Cursor / VS Code / Windsurf):
{
"mcpServers": {
"rrubocop": {
"type": "stdio",
"command": "rrubocop",
"args": ["--mcp"],
"cwd": "/path/to/your/project"
}
}
}
Claude Code:
claude mcp add rrubocop -- rrubocop --mcp
Intended for MCP clients, not interactive use.
Caching
Content-addressed cache under $RRUBOCOP_CACHE_DIR (or $XDG_CACHE_HOME/rrubocop / ~/.cache/rrubocop). Keys cover contents, version, rule revision, --only/--except, --rubocop-version, config fingerprint, and path. Auto-pruned to 20 000 entries; --cache false skips reads but still writes. Autocorrect runs bypass the cache. Nothing is written inside the project.
Benchmarks
Cold lint (cache off) on a large Rails codebase (~3.8k Ruby files), same host:
| Tool | Wall clock | Result | Notes |
|---|
bundle exec rubocop --cache false | 2m 37s (2:36.64) | 0 offenses | ~99% CPU (mostly single-core) |
rrubocop --cache false | 7.0s (7.005) | 0 offenses | ~1040% CPU (parallel) |
nitrocop --no-cache | 8.9s (8.906) | ~21k offenses | ~1000% CPU; not a fair parity run (see below) |
rrubocop is โ 22ร faster wall clock than RuboCop (156.6s / 7.0s) on the same clean result. RuboCop user time was 155s; rrubocop used 71s of CPU across cores.
nitrocop caveat: out of the box it did not honor the same disabled cops / custom rules / gem configs as bundle exec rubocop (e.g. failed inherit_gem load, skipped/unimplemented cops), so it reported ~21k false positives vs RuboCop/rrubocopโs clean run. Matching that baseline needs extra setup (--migrate, plugin/config wiring). Treat the 8.9s figure as raw throughput only, not drop-in parity.
Cold lint (cache off) on rails/rails (main, RuboCop 1.79.2 via bundle exec), same host (4 cores), both clean (0 offenses). Two runs averaged:
| Tool | Wall clock | Notes |
|---|
bundle exec rubocop --cache false | 1m 7s (67.254) | ~99% CPU (mostly single-core); 3543 files |
rrubocop --cache false | 5.7s (5.668) | ~357% CPU (parallel); 3446 files |
โ 12ร faster wall clock (67.254s / 5.668s). RuboCop user time was 66.9s; rrubocop used 20.2s of CPU across cores.
Same host, cold --cache false, offense-set parity on Active Support and Active Record (fp=0, fn=0 on shared .rb files):
| Target | RuboCop | rrubocop | Speedup |
|---|
activesupport (rails main) | 11.566s (531 files) | 1.872s | โ6.2ร |
activerecord (rails main) | 29.611s (1162 files) | 2.780s | โ10.7ร |
activesupport (v8.1.3.1) | 10.729s (506 files) | 1.795s | โ6.0ร |
activerecord (v8.1.3.1) | 27.847s (1126 files) | 2.656s | โ10.5ร |
Cold lint on a fresh rails new app (Rails 8.1.3.1, default rubocop-rails-omakase via inherit_gem, RuboCop 1.90.0 via bundle exec), plus scaffold/model/mailer/job generators (41 identical target files). Offense-set parity fp=0, fn=0 (both clean). Two-run average:
| Tool | Wall clock |
|---|
bundle exec rubocop --cache false | 0.722s |
rrubocop --cache false | 0.593s |
Cross-test vs nitrocop
With a local nitrocop build:
testdata/cross_test_nitrocop.py /path/to/fixture_dir
Compares (path, line, cop_name) offense sets.
License
GPL-3.0-or-later. See NOTICE for MIT attribution of nitrocop-derived design/logic.