← All posts
Make Your AI Slop Emdashes-Free
· 5 min read

Make Your AI Slop Emdashes-Free

AI writing tools love em-dashes. Here is how to detect, remove, and permanently ban them from your codebase using Vale.

Alexis Bouchez

If you have ever shipped a blog post, marketing page, or UI copy written with an LLM, you have shipped em-dashes. Probably dozens of them.

The em-dash (that long dash you see everywhere: -) is the signature punctuation of AI-generated text. GPT, Claude, Gemini - they all reach for it compulsively. It becomes a crutch. Scan any AI-produced paragraph and you will find it peppered between clauses, standing in for commas, colons, and full stops that the model was too lazy to choose.

The result is text that reads fine at a glance but feels vaguely off. It has the rhythm of someone trying too hard. That is the slop tell.

Why AI models overuse em-dashes

LLMs are trained on human writing. Human writers do use em-dashes, but deliberately - for parenthetical asides, sudden interruptions, or dramatic pauses. The models learned the surface pattern but not the discipline behind it.

So they use em-dashes everywhere a pause feels appropriate, which is constantly. The em-dash becomes a filler punctuation, the written equivalent of "um".

The fix is not to tell the model to stop. Prompting helps, but it does not stick. The fix is to make em-dashes a hard error in your toolchain.

Enter Vale

Vale is a prose linter. It runs like ESLint but for text. You configure rules in YAML, point it at your files, and it reports violations.

Setting it up takes three minutes.

1. Install Vale

brew install vale

2. Create a custom style rule

Create styles/YourProject/EMDash.yml:

extends: existence
message: "Em-dash found. Use a hyphen (-) or restructure the sentence."
level: error
tokens:
  - '\u2014'

That Unicode code point is the em-dash character. Vale will flag any file containing it as an error.

3. Configure Vale

Create .vale.ini at your project root:

StylesPath = styles
MinAlertLevel = error

[*.{md,ts,tsx,json}]
BasedOnStyles = YourProject

Adjust the glob to match your content files. This config checks TypeScript, React, JSON, and Markdown files - which is where your UI copy and blog content live.

4. Add an npm script

In package.json:

"lint:prose": "vale ."

Run npm run lint:prose and watch it find every em-dash your AI assistants have smuggled in.

Fixing existing em-dashes

Before Vale can pass, you need to clean up what is already there. A sed one-liner handles the bulk of it:

find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.json" -o -name "*.md" \) \
  -not -path "*/node_modules/*" \
  -exec sh -c 'LC_ALL=C sed -i "" "s/ - / - /g; s/- /- /g" "$1"' _ {} \;

Wait, that will just replace spaces. Here is the real command, using the actual em-dash character:

find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.json" -o -name "*.md" \) \
  -not -path "*/node_modules/*" \
  -exec sh -c 'LC_ALL=C sed -i "" "s/\xe2\x80\x94/ - /g" "$1"' _ {} \;

For title separators like "Documentation - Palmframe", consider using a pipe instead: "Documentation | Palmframe". It looks cleaner and is common SEO practice.

Some em-dashes require manual judgment:

  • List item labels (Pricing - per-user costs): replace with a colon (Pricing: per-user costs)
  • Parenthetical asides (teams - especially startups -): replace with commas (teams, especially startups,)
  • UI null placeholders (a lone em-dash in tables): replace with a hyphen ('-') or 'N/A'

After the bulk sed, run npm run lint:prose again and fix the handful of remaining cases manually.

Adding it to your workflow

Vale catches em-dashes before they reach production. But for this to work, it needs to run automatically.

Two options:

As a pre-commit hook (via Husky or a simple .git/hooks/pre-commit script):

#!/bin/sh
npm run lint:prose

As a CI step in your GitHub Actions workflow:

- name: Prose lint
  run: |
    brew install vale
    npm run lint:prose

Now every AI-assisted commit gets checked. The em-dash becomes impossible to ship.

Document the rule for your AI assistant

If you use Claude Code or another AI coding assistant, add the rule to your CLAUDE.md (or equivalent):

## Prose Linting (Vale)

Vale enforces zero em-dashes across all content files. Run `npm run lint:prose` before committing content changes.

**Rule:** Never use em-dashes (`-`). Use a hyphen (`-`), a colon (`:`) for list item labels, or restructure the sentence.

The AI assistant will read this and avoid the pattern when writing content. Combined with the automated check, you get both prevention and detection.

Why this matters

Em-dashes are not a catastrophe. Nobody unsubscribes because a blog post had too many long dashes. But they are a signal. They tell readers - and yourself - that the text was generated, not written.

The goal of AI-assisted writing is output that reads like you wrote it. That means applying your own voice, your own judgment, and your own punctuation habits. Em-dashes are rarely yours. They are the model's.

Ban them at the toolchain level and you force yourself (and your AI) to write in a way that actually sounds human.

Palmframe is a feedback widget you can embed in two lines of code. Free to start.

Want to start collecting feedback? Try Palmframe for free - takes 2 minutes to set up.