0.0
The project is in a healthy, maintained state
A Rubocop/StandardRB plugin providing automated Rubocop foot-rake feedback to AI coding agents.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 13.0
~> 3.0
~> 1.40

Runtime

>= 1.50
 Project Readme

rubocop-claude

"CUT IT OUT, CLAUDE." doesn't work.

"Oh look, somehow you failed linting, how rough for you, better fix it! no naps!" does.

AI assistants are useful and can write functional code. They also:

  • Love to nap on giant hoards of old code.
  • Build defensive code fortresses EVERYWHERE.
  • Handle explosive errors by adding "okay but what if we just ignored that! look, fixed it!"
  • Add comments everywhere and then pretend you wrote them. 😒
  • Reach for the world's least common Unicode symbols and then get confused when you say "stop it."

This gem tries to make them cut that out.

Installation

Add to your Gemfile:

gem 'rubocop-claude', require: false

Then run:

bundle install
rubocop-claude init

The init command will:

  1. Create .claude/linting.md with instructions for AI assistants
  2. Add rubocop-claude to your .standard.yml or .rubocop.yml

Manual Setup

Add to .standard.yml or .rubocop.yml:

plugins:
  - rubocop-claude

Cops

Cop Description
Claude/NoFancyUnicode Flags emoji and fancy Unicode (curly quotes, em-dashes)
Claude/TaggedComments Requires attribution on TODO/FIXME/NOTE comments
Claude/NoCommentedCode Detects commented-out code blocks
Claude/NoBackwardsCompatHacks Catches dead code preserved "for compatibility"
Claude/NoOverlyDefensiveCode Flags rescue nil, excessive &. chains, defensive nil checks
Claude/ExplicitVisibility Enforces consistent visibility style (grouped or modifier)
Claude/MysteryRegex Flags long regexes that should be extracted to constants
Claude/NoHardcodedLineNumbers Flags hardcoded line numbers that become stale

Configuration

All cops are enabled by default. Configure in .rubocop.yml:

Claude/NoFancyUnicode:
  AllowInStrings: true  # Allow emoji in user-facing strings
  AllowedUnicode:
    - "\u2192"  # Allow specific characters

Claude/TaggedComments:
  Keywords:
    - TODO
    - FIXME
    - NOTE
    - HACK

Claude/NoCommentedCode:
  MinLines: 2  # Only flag multi-line blocks (set to 1 for single lines)
  AllowKeep: true  # Allow KEEP [@handle]: comments to preserve code

Claude/NoOverlyDefensiveCode:
  MaxSafeNavigationChain: 1  # Flag 2+ chained &. operators
  AddSafeNavigator: false  # Autocorrect to &. instead of direct call

Claude/ExplicitVisibility:
  EnforcedStyle: grouped  # or 'modifier' for `private def foo`

Claude/MysteryRegex:
  MaxLength: 25

Claude/NoHardcodedLineNumbers:
  CheckComments: true   # Check comments (default: true)
  CheckStrings: true    # Check string literals (default: true)

Why These Cops?

NoFancyUnicode

Me: "Okay, let's log that success." Claude: A RAINBOW OF OBSCURE SYMBOLS EMERGES FROM THE MISTS.

# bad
puts "Deployment successful! 🚀"
logger.info "Task completed ✅"

# good
puts "Deployment successful!"
logger.info "Task completed"

TaggedComments

"You added a comment, and it looks like I added a comment, and that comment is wrong."

# bad
# TODO: fix this later

# good
# TODO: [@username] fix this later

NoCommentedCode

"No hoarding."

# bad
# def old_implementation
#   do_something_outdated
# end

# good - just delete it

NoBackwardsCompatHacks

"NO HOARDING."

# bad
_old_value = previous_calculation  # keeping for reference
OldName = NewName  # backwards compatibility

# good - delete it

NoOverlyDefensiveCode

"YOU ARE BUILDING A DOOMSDAY BUNKER FOR A METHOD THAT CAN'T FAIL, CLAUDE."

# bad
result = dangerous_call rescue nil
user&.profile&.settings&.value
user && user.name

# good
result = dangerous_call
user.profile.settings.value
user.name

ExplicitVisibility

"The solution is NOT 'make all the private methods visible,' bud."

# grouped style (default)
class Foo
  def public_method; end

  private

  def private_method; end
end

# modifier style
class Foo
  def public_method; end
  private def private_method; end
end

MysteryRegex

"I have no idea what your 300-character regex does, Claude, because you dumped that on the screen, said 'okay cool fixed' and then got distracted."

# bad
if input.match?(/\A[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\z/)

# good
EMAIL_PATTERN = /\A[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\z/
if input.match?(EMAIL_PATTERN)

NoHardcodedLineNumbers

"That line reference is wrong, Claude. The code moved."

# bad
# see line 42 for details
# Error at foo.rb:123
raise "check line 55"

# good
# see #validate_input for details
# Error in FooError class
raise "check validate_input method"

Suggested RuboCop Defaults

rubocop-claude init also enables some defaults in Rubocop that are aimed at keeping AI coders from getting weird with their Ruby. Full config with rationale in config/default.yml.

Cop Setting Why
Style/DisableCopsWithinSourceCodeDirective Enabled AI "fixes" linting by disabling cops. No.
Layout/ClassStructure Enabled AI scatters methods randomly. Enforce order.
Style/CommentAnnotation RequireColon: true Works with TaggedComments.
Lint/Debugger Enabled AI leaves binding.pry in code.
Layout/MultilineMethodCallIndentation indented Leading dot, 2-space indent.
Style/SafeNavigation MaxChainLength: 1 Complements NoOverlyDefensiveCode.
Metrics/CyclomaticComplexity Max: 7 Flag spaghetti logic.
Metrics/AbcSize Max: 17 Flag bloated methods.
Metrics/MethodLength Max: 10 AI writes 80-line methods. 10 is plenty.
Metrics/ClassLength Max: 150 Catches god classes.
Metrics/ParameterLists Max: 5 Too many params = needs refactoring.
Style/GuardClause Enabled Early returns > nested conditionals.
Style/RedundantReturn Enabled Ruby returns last expression.
Style/MutableConstant strict Always .freeze constants.
Lint/UnusedMethodArgument Enabled Dead params = dead code.
Style/NestedTernaryOperator Enabled a ? (b ? c : d) : e is unreadable.
Style/OptionalBooleanParameter Enabled foo(data, true) - what's true mean?
Naming/MethodParameterName MinNameLength: 2 No x, y, z params. Use real names.
Style/ParallelAssignment Enabled One assignment per line.

Not enabled: Lint/SuppressedException and Style/RescueModifier - our NoOverlyDefensiveCode covers these with a unified "trust internal code" message.

Claude Integration

rubocop-claude init will add files to .claude (or elsewhere if you provide it with a path).

These files are a bunch of reference docs for AI agents to gnaw on when they're asked to think about linting. It does not change any of your other config, and it does not try to integrate with the rest of your setup. It's just providing a structured starting point for getting AI agents to lint more effectively.

WHAT THIS DOES NOT DO

This isn't a subsitute for reviewing your code or monitoring AI assistants. Static analysis is a wonderful tool for wrangling Ai coders, but it does not replace reviewing and monitoring changes.

Trying to force static analysis tools to fully handle every single edge case is silly, and trying to make these weird, enthusiastic pattern recognition engines get everything right on the first try isn't going to work. What we're trying to do is give tools like Claude a way to efficiently remember that they shouldn't be making weird decisions, and that they should make good decisions instead.

We can't always prevent AI tools from charging off in weird directions, but we CAN scatter rakes in their way and make them stop and think. This adds more rakes.

Development

bundle install
bundle exec rspec
bundle exec rubocop

License

MIT License. See LICENSE.txt.