When AI agents like Claude Code write code, they often leave trailing whitespace for no reason. Tabs or spaces? Doesn’t matter. It’s bad.

Claude leaving trailing whitespace

Whyyyyy?

LLMs generate trailing whitespace because they produce text token by token without visual feedback. They ’learn’ from training data containing inconsistent whitespace patterns.

Telling Claude what to do isn’t enough

I used to have something like this in my CLAUDE.md:

NEVER EVER leave trailing whitespace at the end of lines.

But as with many things added as context in CLAUDE.md, Claude Code can be very inconsistent about following them. Does that happpen as the context window fills up? I don’t know. I want something more deterministic here, though.

Holding Claude’s hand via Hooks (for Ruby)

At the beginning of July, Anthropic introduced Hooks.

Claude Code hooks are user-defined shell commands that execute automatically at specific points in Claude’s agent loop. They intercept the agent’s workflow, allowing you to add validation, transformation, or blocking logic. Hooks run with your current environment credentials and can modify or prevent actions before they complete.

I add this to ~/.claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "^(Edit|MultiEdit|Write)$",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r 'select(.tool_name | test(\"^(Edit|MultiEdit|Write)$\")) | .tool_input.file_path | select(test(\"\\\\.(rb|rake|gemspec)$\"))' | xargs -I {} rubocop --auto-correct --only Layout/TrailingWhitespace {} 2>/dev/null"
          }
        ]
      }
    ]
  }
}

Now Rubocop cleans things up if needed after every Ruby file edit!

OK, but why Rubocop and not just sed?

I do it this way because I usually write Ruby and if I did this with, say, regexes and did it for every file Claude edits I could run into trouble!

Generic whitespace removal with sed or regex risks corrupting binary files, breaking Markdown line breaks, or damaging whitespace-sensitive formats (e.g., Python). Language-specific tools understand—they know when trailing spaces matter and when they don’t.

RuboCop’s Layout/TrailingWhitespace cop does one thing. It removes trailing whitespace for Ruby files. Only Ruby. The hook runs for me after every file edit, holding Claude’s hand while I’m grabbing coffee. I might have it run all “safe” cops in the future.

How Claude Code Hooks work

Hook Types

Five events trigger hooks:

  • PreToolUse: Executes before tool calls. Can block actions.
  • PostToolUse: Runs after tool calls complete.
  • Notification: Triggers when Claude sends notifications.
  • Stop: Executes when Claude finishes responding.
  • SubagentStop: Runs when subagent tasks complete.

Hooks execute shell commands. They can match specific tools (e.g. "WebFetch|WebSearch", "Edit|MultiEdit|Write") or apply globally ("" matches all tool uses regardless of type). Common uses include automatic code formatting, logging commands, sending notifications, and blocking sensitive file edits.

Configure hooks through the /hooks command or in settings files. They require careful security review since they run automatically during Claude’s workflow.

Other uses

Getting Claude to consistently lint, run tests etc. is a pain. I’ll probably wrap that in a standard make target and use that everywhere. For now, one could do something like:

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "^(Edit|MultiEdit|Write)$",
      "hooks": [{
        "type": "command",
        "command": "[ -f Makefile ] && grep -q '^lint:' Makefile && make lint || true"
      }]
    }]
  }
}

Other ideas:

  • Making sure certain tests always run
  • Run gofmt or similar for other languages
  • Anything procedural and easily automated you have spelled out in CLAUDE.md
  • For compiled languages, e.g. Java, golang, C++ just keeping your project compiling after every edit has sped me up considerably and produced more useful “vibe coded” outputs with less intervention and prompting from me.
  • Blocking git commit --no-verify (ht to @jtdowney@infosec.exchange)

Safety Considerations

Hooks execute with full user permissions. Review all hook commands carefully—they can delete files, access networks, or run arbitrary code. Never copy hook configurations without understanding each command. Malicious hooks could compromise your system when Claude performs routine operations.