The problem: Nobody sits down meaning to write an em dash or a straight quote into a draft, it
just creeps in, one write at a time, until the tells are everywhere and you’ve stopped noticing
them. The usual fix is a cleanup pass after the fact: catch what you can, hope you got it all.
ww-copy-sweep skips the pass entirely: the write fails before the tell ever exists.
ww-copy-sweep is a PreToolUse hook. It has no command because you never invoke it; it fires on
every Write/Edit/MultiEdit to a matching file, scans the new content before it’s saved, and
blocks the write if it finds a violation, and Claude gets a line-numbered list of exactly what’s
wrong and fixes it inline, same turn, before the bad version ever exists on disk. Which is a good
moment to admit that I built this, then ran it on the repo you’re reading, and found 272 em
dashes I’d apparently written myself without noticing across every file in here, so any rule in
this doc that sounds strict is strict because I’ve already been on the wrong end of it.
Deterministic, on by default, zero model calls:
| Check | Catches |
|---|---|
em_dash | A budget, not a ban: 0 allowed under 400 words, 1 allowed above it, and that one still has to earn its place |
straight_quote | " where a curly quote “ ” belongs |
title_case | ## The Big New Feature Launch instead of ## The big new feature launch |
banned_phrase | Corporate-speak: leverage, synergy, touch base, cutting-edge, excited to announce, and more |
throat_clearing | “As you know,” “We’re excited to announce” |
summary_crutch | “Bottom line:”, “Key takeaways:” |
we_pronoun | Off by default: a bare We in solo-voice copy, opt in if you write alone |
Semantic, off by default, needs a model call: vague_pronoun, rule_of_three, pontificating,
staccato_cadence.
These catch patterns a regex genuinely can’t; full explanation of the tradeoff (nondeterminism,
latency, small API cost, fail-open by design) is in [the skill’s own
doc](skills/ww-copy-sweep/SKILL.md#semantic-checks-off-by-default-read-this-first).
Easiest, just tell Claude:
> Install ww-copy-sweep from https://github.com/lee-fuhr/claude-operator-skills and walk me through my config options.
Claude does the file copy and the settings.json edit itself, then asks a handful of plain
questions instead of handing you env var names to memorize: scope it to one folder or the
whole repo, catch stray “We”s or not, turn on the deeper AI-graded checks or skip them, what
counts as “long” for you. Full walkthrough script for Claude to follow is in [the skill’s own
doc](skills/ww-copy-sweep/SKILL.md#install-just-tell-claude).
Manual, if you’d rather do it yourself:
mkdir -p ~/.claude/hooks
cp skills/ww-copy-sweep/hooks/copy_sweep.py ~/.claude/hooks/copy_sweep.py
chmod +x ~/.claude/hooks/copy_sweep.py
Then add this to ~/.claude/settings.json (or .claude/settings.json for one repo only):
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{ "type": "command", "command": "python3 ~/.claude/hooks/copy_sweep.py" }
]
}
]
}
}
Restart Claude Code. Full config reference (scoping to a drafts/ folder, turning we_pronoun
on, the one-off bypass flag) is in skills/ww-copy-sweep/SKILL.md.
Part of Operator Skills, a collection of skills for running a real Claude Code setup.