slop-chop
Chop the slop. Paste in text and get back something that reads like a person wrote it.
AI writing leaves fingerprints. It runs on em-dashes, drops a semicolon into every other
sentence, reaches for words like comprehensive and substrate, and clears its throat
with openers like "In summary" or "Giving it to you honestly." slop-chop pulls all of
that out in a single pass. You can also hand it your own list of things to cut, so the
result reads like you instead of a chatbot.
Why
Cleaning this up by hand is tedious, and asking the model to "stop using em-dashes" holds
for about three sentences before it forgets. slop-chop just takes the text and cleans it,
the same way every time.
How it works
There are two passes, and you can run either one on its own.
The first is a rules pass. It is fast and deterministic. It swaps characters, drops words
you have flagged, rewrites stock phrases, and tidies the punctuation, with no model, no
cost, and the same output on every run. It knows markdown, so fenced code blocks and
inline backtick spans come through untouched.
The second is an optional rewrite pass that hands the text to a model for the things
rules cannot manage, like reworking a sentence so it no longer needs a semicolon, or
nudging the writing toward a voice you picked.
ENGINE.md has the details if you want them.
Install
go install github.com/dcadolph/slop-chop@latest
Or clone and build:
git clone git@github.com:dcadolph/slop-chop.git
cd slop-chop
go install .
Usage
# Print the cleaned text to stdout. Your file is not changed.
slop-chop fix notes.md
# Clean the file in place, like gofmt -w.
slop-chop fix -w notes.md
# Pipe text through it
echo "In summary, a robust—and seamless—result." | slop-chop fix
# Flag slop without changing anything (exits non-zero if it finds any)
slop-chop check notes.md
# Check or fix several files at once
slop-chop check docs/intro.md docs/guide.md README.md
slop-chop fix -w docs/intro.md docs/guide.md
# Enforce a spelling variant: flag or fix the other dialect
slop-chop check --dialect american notes.md
slop-chop fix --dialect british notes.md
# Use your own profile
slop-chop fix --profile myprofile.json notes.md
# Get findings as JSON for other tools to read
slop-chop check --json notes.md
slop-chop check --json --pretty notes.md
# Get the cleaned text and the findings together
slop-chop fix --json notes.md
# Deeper clean: rules first, then a model rewrite (needs ANTHROPIC_API_KEY)
slop-chop fix --rewrite notes.md
slop-chop fix --rewrite --model claude-sonnet-4-6 notes.md
# Ask a model to check the rewrite kept your meaning (a second, paid call)
slop-chop fix --rewrite --verify notes.md
check --json prints a {"findings": [...]} object to stdout, and fix --json adds the
cleaned text as {"cleaned": "...", "findings": [...]}. Each finding carries the rule,
the matched text, the suggested replacement, and a line and column.
Modes
check flags what it finds and exits non-zero. Drop it in CI.
fix writes the cleaned text to stdout and leaves your file alone. Pass -w to change
the file in place instead.
Use it in CI
Add a workflow that fails a pull request when it finds slop:
name: slop-chop
on: pull_request
jobs:
slop:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dcadolph/slop-chop@v0.8.0
with:
files: docs/intro.md docs/guide.md
# profile: myprofile.json # optional
Or have it fix the files and push the cleanup back to the pull request branch:
name: slop-chop
on: pull_request
jobs:
slop:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- uses: dcadolph/slop-chop@v0.8.0
with:
files: docs/intro.md docs/guide.md
mode: fix
commit: "true"
# message: Chop the slop # optional commit message
Rewrite pass (optional)
The rules pass is deterministic and free. For the work rules cannot do, like reworking a
sentence so it no longer needs a semicolon or bending the text toward your voice, add
--rewrite. It runs the rules first, then hands the result to a model.
export ANTHROPIC_API_KEY=sk-...
slop-chop fix --rewrite notes.md
slop-chop fix --rewrite --model claude-sonnet-4-6 notes.md
It defaults to Claude Opus 4.8. Set the voice it aims for with the tone list in your
profile. This pass costs money and the output varies from run to run, so the rules pass
stays the default.
The model's reply is verified before you get it. The rules run over it again to clean any
tell the model slipped back in, and slop-chop warns on stderr if the reply kept a buzzword
or changed a code block from your input. It also diffs the load-bearing tokens, numbers,
percentages, money, links, and acronyms, so a changed figure or a dropped link is flagged
as a likely fact change. All of that is deterministic and free.
For the meaning a token diff cannot see, a flipped negation or a softened claim, add
--verify. It makes a second model call that compares the rewrite against your original
and reports any change in meaning on stderr. It costs another call and is only as reliable
as the model, so it is off by default and warns rather than fails.
Two flags go further. --verify-strict makes a flagged change fail the command with a
non-zero exit, so CI can catch drift. The rewrite still gets written first. --verify-retry N gives the model another go: when the check flags a change, slop-chop tells it which facts
it changed and asks for the rewrite again, up to N more times, until the check passes. With
--json, the verdict shows up in the report as a verify object, so you can read it
instead of watching stderr.
# Fail CI when the rewrite drifts from your meaning.
slop-chop fix --rewrite --verify --verify-strict notes.md
# Give the model two more tries to keep the facts it changed.
slop-chop fix --rewrite --verify --verify-retry 2 notes.md
Spelling
Pass --dialect american to flag British spellings and rewrite them, or --dialect british for the reverse. It is off by default. In check a foreign spelling is a finding
like any other, so it fails the CI gate. In fix it is rewritten in place, with the
capitalization kept, so Behaviour becomes Behavior and BEHAVIOUR becomes BEHAVIOR.
Code spans and fenced blocks are left alone, so colour in a CSS sample stays as written.
The swap is a word-for-word lookup against a built-in list, not a suffix rule, so words
that share an ending but no dialect difference, like size or advertise, are never
touched. American mode is the fuller of the two. Some words spell one dialect as a word
that means something else in the other, like cheque and check or tyre and tire.
Those rewrite only toward American, so British mode never turns a plain check into a
cheque.
One call it makes on purpose: -ize endings like organize are treated as American even
though British writing accepts them too, so --dialect british rewrites organize to
organise. If your house style keeps -ize, leave the dialect off.
A repo can pin a dialect in its profile with "dialect": "american", and the flag
overrides it for a single run.
Style profiles
A profile is a small config file that lists what to cut and what to put in its place:
characters, words, phrases, and a couple of notes on tone. Keep your own and point the
tool at it with --profile, or name it .slop-chop.json in the directory you run from
and it gets picked up on its own.
Status
Still early, but the core is in place. The rules pass is built and working. The rewrite
pass is built too and sits behind the --rewrite flag, because it needs an API key and
costs money, so the free, predictable rules pass stays the default. The one part not yet
exercised is a live rewrite run against the real API.
License
MIT. See LICENSE.