Claude Code plugin
Stops Claude inventing shorthand for its shorthand.
Opus 5 writes like it is compressing notes for itself and forgetting anyone else has to read them. It coins a term, then coins a shorter term for that term, and three sentences later you are maintaining a glossary of words that exist nowhere but this conversation.
The registry row had five layers of superseded state, so I rewrote it canonical-truth-first. The two-field change set is done and the blank-means-ours wording now hangs off the caller.what it meant
I replaced the status notes with what is actually true now. The caller adds the name when the field is blank.
Every invented noun is a debt the reader pays. You cannot look up blank-means-ours. It was made up forty tokens ago.
Words that perform precision instead of being precise. Each one has an older, shorter word that does the same job.
The hyphenated garbage problem. Terms invented mid-sentence that the reader has never seen and cannot look up.
Performed humility in place of an answer. If it was wrong, say what is right now. That is the whole apology.
Earns its place only when it changes what you do next. Otherwise it is padding with a serious face on.
Plus binary flips (“it's not X, it's Y”), pseudo-technical hedging (modulo, non-trivial, by construction), and the standard AI vocabulary (seamless, robust, leverage, utilize, delve).
Nothing, when the reply is clean. When it trips, one line at the bottom:
The label says jargon rather than a clever name, because a plugin arguing “stop inventing vocabulary” should not open by inventing vocabulary.
Three parts, and the third feeds the second. That loop is the whole idea.
UserPromptSubmit hook
Puts the rules back in front of the model on every turn, immediately before it writes.
MessageDisplay hook
Scores what actually came out and appends the receipt line. Then hands the score back to step 2.
Clean replies get no reminder at all, because context is not free. Once the linter catches something, the next injection names the exact words and their counts. Keep drifting and it gets blunter. Stop and it goes quiet again.
This is the part people get stuck on, and it is not a prompting skill issue.
CLAUDE.md is read once, at the top of the session. Forty exchanges later it sits under a hundred thousand tokens of transcript, competing with everything that arrived after it. The instruction is still technically in context. It is just no longer the loudest thing there.
A UserPromptSubmit hook injects the same words immediately before the model writes, every single turn. Same rules, different position, completely different result.
Observed live. After one flagged reply, the model was told to avoid load-bearing, seam and fair pushback. Asked on the next turn what it had been warned about, it answered: “the one about a joint bearing weight, the one about a joint between two parts, and the two-word concession about your objection being fair.”
It would not reuse the words even to name them.
The first version flagged any hyphenated compound as coinage. That caught canonical-truth-first. It also caught first-party, logged-in, click-through and back-button.
Measured against a corpus of real human writing, it scored 66 on a document written entirely by hand. A linter that noisy gets switched off within a day, which makes it worth exactly nothing.
Now only compounds of three or more parts count as coined. Two-word compounds are ordinary English, and named offenders like load-bearing are listed explicitly in the lexicon instead, where they can be exact.
Code fences, inline code, URLs, file paths and markdown image alt text are stripped before scanning. A variable named seam is not a writing problem.
The original example, the two-field change set, is not caught. It is a two-word compound, and two-word compounds are ordinary English.
What made that phrase bad was never its shape. It was that it named something the reader had never been told about. No regular expression can see that. That half is the output style's job, and the output style is a nudge, not a guarantee.
The honest risk. Banning words can produce circumlocution instead of clarity. A model told never to write load-bearing may write the joint bearing weight, which is worse. If replies start reading like a hostage note, soften the correction from “never use these” to “prefer the plain word.”
Both hooks fail open. On any problem at all, malformed JSON, a missing lexicon, an unwritable cache, a bug in the plugin itself, they print nothing and exit 0, which leaves the original text on screen untouched.
A display hook that can swallow an answer is worse than no display hook, so ten of the forty tests exist only to prove this.
Nothing leaves the machine. There is no model call and no network request. The scanner is regular expressions over the finished message, which is why it costs about 20 milliseconds instead of the 45 seconds a local model needs.
claude plugin marketplace add <path-to-repo>
claude plugin install opus-but-good@opus-but-good-marketplace
Then turn on the output style in /config under Output style. The hooks work without it; the style is the optional half.
| Command | Does |
|---|---|
/plain | Say that last answer again in plain English. Keeps every fact, expands every coined term. |
/obg | Session report. How many replies were flagged, which words came up most. |
| OBG_MODE | Behaviour |
|---|---|
flag | Default. Streams normally, one receipt line when it trips. Silent when clean. |
strip | Applies the swap table and shows the cleaned text. No added latency, since the swaps are regex. |
silent | Scores everything, shows nothing. The loop still runs. |
off | Nothing at all. |
| Variable | Default | Meaning |
|---|---|---|
OBG_ENABLED | 1 | Master switch. |
OBG_LABEL | jargon | The word on the receipt line. |
OBG_MIN_SCORE | 2 | Receipt threshold. |
OBG_INJECT | 1 | Set 0 to keep the linter but stop injecting rules. |
OBG_LEXICON | bundled | Point at your own word list. |
Everything is tunable in lexicon.json. Each group has an on/off flag and a weight. One group ships disabled because its words are also real words: contract, primitive, harness, canonical, invariant, plumbing.
Built because the complaint is real and widely shared: r/ClaudeCode, “Opus 5 is exhausting”.
No tracking, no analytics, no fonts loaded from anywhere.