# slop-detector
Configurable AI-slop linter for PRs and committed content. Catches the recognisable tells of agent-generated text: leaked tool-call XML wrappers, em-dashes in user-facing prose, hedging openers, marketing adjectives, doubled summary headings.
Part of [agent-dx](https://github.com/LanNguyenSi/agent-dx), playbooks and tooling for teams shipping with AI agents.
## Why
Agents leave fingerprints. Some are objectively wrong: `` artefacts from MCP serialisation, doubled `## Summary` blocks. Others are stylistic tells the team has already decided to avoid: em-dashes in prose, `It is important to note` openers, empty marketing adjectives. This package turns those rules into a deterministic linter you can run in pre-commit, in CI, or against a directory tree.
## Install
slop-detector is not yet published to npm (the bare `slop-detector` name there is an unrelated third-party package), so run it from a local build of this monorepo:
```bash
git clone https://github.com/LanNguyenSi/agent-dx
cd agent-dx
cd packages/slop-detector && npm install && npm run build && cd ../..
# alias the local CLI for this shell; the examples below use the bare `slop-detector` command
alias slop-detector="node $PWD/packages/slop-detector/dist/cli.js"
```
Without the alias, invoke the built CLI directly: `node packages/slop-detector/dist/cli.js check README.md`.
## Quick start
```bash
# scan a path (file or directory)
slop-detector check packages/
# scan stdin (use in pre-commit pipes)
git diff --cached --name-only | xargs cat | slop-detector check --stdin-path PR_BODY.md
# only run a specific pack
slop-detector check . --pack agent-tics
# see why a rule fires
slop-detector check . --explain
# JSON output for tooling
slop-detector check . --format json
```
## Rule packs
Each pack groups related rules. Enable or disable per repo via `slop.config.yml`.
| Pack | Default | Catches |
|------|---------|---------|
| `agent-tics` | on | Stray `` / `` tags, auto-appended Claude Code footers, doubled Summary headings, template TODO placeholders |
| `prose-slop` | on | Em-dashes in prose, hedging openers, empty marketing adjectives, signature LLM idioms like `delve into`, `tapestry of`, `leverage the power of` |
| `comment-slop` | off | JSDoc on trivial getters, comments that restate the next line, orphan markers (`// removed`, `// kept for backcompat`), comment-heavier-than-body helpers, ASCII banner dividers |
| `code-slop` | off | try/catch around code that cannot throw, default values on required-typed params, empty/rethrow catches, async without await, backcompat shims for unreleased APIs |
| `ui-slop` | off, opt in via `--pack ui-slop` | Gradient text, purple+cyan AI palettes, animated layout properties, skipped heading levels, monospace-everywhere, flat type hierarchy. Scans CSS / SCSS / LESS / HTML / JSX. |
Run `slop-detector list-rules` for the full rule catalogue with severities and rationales.
### `ui-slop` (M3 v1) by example
Opt in with `--pack ui-slop`. Examples that trip the four default-on rules:
```css
/* ui-slop/gradient-text */
.headline {
background: linear-gradient(90deg, #7c3aed, #06b6d4);
-webkit-background-clip: text;
color: transparent;
}
/* ui-slop/ai-color-palette */
.hero {
background: radial-gradient(circle, hsl(270, 70%, 50%), hsl(185, 80%, 50%));
}
/* ui-slop/animate-layout-properties */
@keyframes grow {
from { width: 100px; }
to { width: 200px; }
}
.panel { transition: height 0.3s ease; }
```
```html
```
The two off-by-default info rules (`ui-slop/monospace-everywhere`, `ui-slop/flat-type-hierarchy`) need an explicit `rules..enabled: true` in `slop.config.yml` or a CLI override; they remain off because both have legitimate counter-uses (technical-product landing pages, mature design systems with subtle steps).
Known v1 limitations (tracked as M3 follow-ups):
- Tailwind class strings like `bg-gradient-to-r from-purple-500 to-cyan-500` are not detected; only literal CSS / hex / hsl in style declarations.
- JSX inline `style={{ background: 'linear-gradient(...)' }}` literals are not scanned for rules 1-3 (only `ui-slop/skipped-heading-levels` walks JSX).
- Vue / Svelte single-file-component `