Remove em-dashes from LLM-generated content in a grammatically correct manner
LLMs love em-dashes—they use them constantly—sometimes multiple times in a single sentence. This can make AI-generated content feel repetitive—and stylistically monotonous.
demdash solves this—quite elegantly, if I say so myself—by using an LLM to intelligently rewrite text—replacing em-dashes with more varied punctuation—while preserving the original meaning.
-What’s striking isn’t the complexity—it’s the pauses—the little hesitations where certainty should live—where context leaks out and assumptions rush in to fill the gap. You can feel it when you read the docs—when a clause trails off—when an em dash does the work of a missing decision—signaling thoughtfulness—while quietly avoiding commitment.
+What’s striking isn’t the complexity; it’s the pauses, the little hesitations where certainty should live, where context leaks out and assumptions rush in to fill the gap. You can feel it when you read the docs, when a clause trails off, when a parenthetical or colon does the work of a missing decision, signaling thoughtfulness while quietly avoiding commitment.npm install demdash
# or
bun add demdashimport { demdash } from 'demdash';
const text = "The package is simple—just one function—and it works great.";
const result = await demdash(text);
console.log(result);
// "The package is simple (just one function) and it works great."export OPENAI_API_KEY=your-key-here
bunx demdash --exampleThis picks a random sample text and shows before/after.
Process a file:
bunx demdash input.txt > output.txtProcess piped input:
echo "Text with em-dashes—like this—gets fixed." | bunx demdashUse a specific model:
bunx demdash --model gpt-4o input.txtView help:
bunx demdash --help| Variable | Description | Default |
|---|---|---|
OPENAI_API_KEY |
OpenAI API key (required if not passed in options) | - |
OPENAI_BASE_URL |
Custom API base URL | OpenAI default |
DEMDASH_MODEL |
Model to use. gpt-5 is more nuanced |
gpt-4o-mini |
DEMDASH_MAX_TURNS |
Maximum LLM calls per invocation | 3 |
interface DemdashOptions {
apiKey?: string; // OpenAI API key
model?: string; // Model to use (default: 'gpt-4o-mini')
maxTurns?: number; // Max retry attempts (default: 3, max: 10)
baseURL?: string; // Custom API base URL
provider?: LLMProvider; // Custom LLM provider
}Options take precedence over environment variables.
const result = await demdash(text, {
apiKey: process.env.MY_OPENAI_KEY,
model: 'gpt-4o',
maxTurns: 5,
});Use any LLM by implementing the LLMProvider interface:
import { demdash, LLMProvider } from 'demdash';
const myProvider: LLMProvider = {
async complete(prompt: string): Promise<string> {
// Call your preferred LLM
const response = await myLLMClient.generate(prompt);
return response.text;
}
};
const result = await demdash(text, { provider: myProvider });- Detection: Checks if the input contains em-dashes (—), en-dashes (–), or double-hyphens (--)
- Prompt Construction: Builds a prompt instructing the LLM to replace dashes with alternative punctuation (commas, parentheses, colons, or periods)
- LLM Call: Sends the prompt to the configured LLM
- Verification: Checks if dashes remain; if so, retries up to
maxTurnstimes - Result: Returns the cleaned text
Look, I personally love em-dashes. I've used them heavily since my time building Medium—they're elegant, expressive, and versatile. But em-dashes have become a telltale sign of AI-generated content, and not everyone shares my enthusiasm for them.
In case it’s not obvious, this library is a joke. But if you genuinely need to de-AI your text, here you go.
So next time you read something and think, "AI wrote this—it has a lot of em dashes," ask yourself: Is it AI? Or is it just a poet trying to give you vertigo in four lines or fewer?
MIT