# 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

Title

Subtitle

``` 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 `