Artificial Intelligence

Claude Keeps Repeating Itself? Stop It with Hooks

15 July 2026 Mehdi 06:37
Claude Keeps Repeating Itself? Stop It with Hooks

If you use Claude daily, you’ve probably noticed it keeps falling back on the same phrases. “Load-bearing”, “honest take”, “you’re absolutely right”: these expressions get old fast. The good news is there’s a technical fix built right into Claude Code.

Why Does Claude Keep Using the Same Expressions?

This isn’t an isolated bug. It’s a side effect of reinforcement learning from human feedback (RLHF). Models like Claude learn to maximize human approval, and certain phrasings end up recurring because they were reinforced during training.

Developers using Claude in professional workflows hit this wall quickly. The model feels capable, but its stylistic range stays locked in place. Getting it to adopt a different tone through plain language instructions is an uphill battle.

Johanna Larsson published a concrete, immediately actionable solution on jola.dev. The post earned 463 points on Hacker News with 519 comments, which confirms the frustration is widespread.

The Fix: Claude Code’s MessageDisplay Hook

Claude Code exposes a configurable hooks mechanism. These hooks let you intercept the model’s output before it’s displayed and apply transformations to it. That’s exactly what you need to filter out unwanted vocabulary.

The approach relies on three things:

  • A Python script that reads Claude’s JSON output, applies expression substitutions, and returns the modified text
  • An executable file placed in ~/.claude/hooks/
  • An entry in ~/.claude/settings.json to wire the hook to the MessageDisplay event

Writing the Replacement Script

Here’s the script from Johanna Larsson, with a few comments added for clarity:

#!/usr/bin/env python3
import json, re, sys

replacements = {
    "seam": "whatchamacallit",
    "you're absolutely right": "I'm a complete clown",
    "honest take": "spicy doodad",
    "load-bearing": "cooked"
}

data = json.load(sys.stdin)
text = data.get("delta") or ""

for phrase, replacement in replacements.items():
    pattern = r"\b" + re.escape(phrase) + r"\b"
    text = re.sub(pattern, replacement, text, flags=re.IGNORECASE)

print(json.dumps({
    "hookSpecificOutput": {
        "hookEventName": "MessageDisplay",
        "displayContent": text,
    }
}))

Save this script to ~/.claude/hooks/wordswap.sh and make it executable:

chmod +x ~/.claude/hooks/wordswap.sh

Wiring the Hook into Claude Code

Open your ~/.claude/settings.json file and add the following block to the hooks section:

{
  "hooks": {
    "MessageDisplay": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "$HOME/.claude/hooks/wordswap.sh"
          }
        ]
      }
    ]
  }
}

Hooks are loaded at session startup. Just open a new Claude Code session and the replacements will be active.

Going Further: Constraining Claude with External Tools

The hooks approach reflects a broader shift among developers: stop trusting prose instructions to control model behavior, and use external, reproducible tools instead.

A post on zernie.com, also noticed on Hacker News, takes this logic even further. It proposes replacing stylistic rules in CLAUDE.md with real linting rules. The idea is straightforward: if you want a behavior to be reliable, don’t hand it off to a natural language instruction. Encode it in a tool that leaves no room for interpretation.

Both approaches complement each other well. Hooks intercept output in real time. Linting acts upstream on the code or text being produced. Together, they give you granular control over what Claude actually generates.

Key Takeaways

  • Claude tends to repeat characteristic expressions because of RLHF: it’s structural, not random.
  • Claude Code’s MessageDisplay hook lets you intercept and replace these expressions before they’re displayed, using a simple Python script.
  • Configuration lives in ~/.claude/settings.json and kicks in when you start a new session.
  • Adapting the replacement dictionary to your own pet peeves is immediate: just edit the script.
  • The broader trend is clear: developers prefer constraining LLMs with tools rather than prompts, especially in production.

If you work with Claude in professional pipelines, these mechanisms are worth exploring seriously. Got a different approach for controlling your models’ stylistic behavior? I’d love to hear about it, feel free to reach out or follow the blog for upcoming articles on the topic.

Sources

Read Also

Leave a comment

Your email address will not be published. Required fields are marked *