dmmulroy/anti-slop: Opinionated Oxlint Rules That Reject Low-Evidence TypeScript
Opinionated Oxlint rules for rejecting low-evidence TypeScript and JavaScript patterns
At a glance
- What is it?
- Anti-slop is a vendored Oxlint plugin, not an npm package. It rejects patterns like chained type assertions, unknown parameters and module mocking, and it expects you to edit the rules after copying them.
- Who is it for?
- Adopt anti-slop if your team already runs Oxlint, writes TypeScript at boundaries, and is willing to own a vendored plugin whose rules you edit. Skip it if you need a versioned dependency with a release lifecycle, if you rely on Vitest module mocking, or if you use Effect and do not want Effect-specific policy enforced.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 6 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 16, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What anti-slop rejects, and who it is written for
The README is direct about the premise: anti-slop is "first and foremost the ruleset I use with my work, projects, and team. It reflects my preferences and taste rather than attempting to be a universal coding standard." That sentence should decide whether you keep reading. This is not a neutral style guide with a compatibility committee behind it. It encodes one maintainer's position that a certain class of TypeScript and JavaScript code is low-evidence: code that widens a known value to unknown, asserts a type into existence, mocks a module instead of creating a seam, or narrows with typeof at the point of use rather than parsing at the boundary.
The rules target a specific author. Someone who already treats the type boundary as the place where untrusted data becomes typed data, and who wants a linter to fail the build when that discipline slips. The rule names read like a list of escape hatches being closed: no-unknown-parameters, no-unknown-returns, no-unknown-type-aliases, no-chained-type-assertions, no-widen-then-assert, no-runtime-typeof, no-reflect-get, no-reflect-apply, no-module-mocking. If your codebase is comfortable with any of those, the first lint run will be loud, and that is the intended experience.
The project is not a framework, a runtime library or a type utility. It is a plugin that Oxlint loads, plus a set of agent skill files that automate the copy and configuration. The repository layout confirms that shape: src/ holds the rules, skills/ holds the agent skill, scripts/ holds a sync script, and there is no published package to install.
How the plugin loads into Oxlint and what the rules actually inspect
Anti-slop runs as an Oxlint JS plugin. You register a copied entry point under jsPlugins with the name anti-slop, then enable individual rules by their prefixed names in the rules map. There is no separate runner and no wrapper CLI. The rules execute inside whatever Oxlint invocation your project already has, which means the cost of adopting anti-slop is the cost of Oxlint plus the maintenance of the vendored directory.
The rules fall into recognizable groups. Some are structural: no-array-filter-map rejects adjacent eager array filter and map passes while allowing lazy iterator pipelines, and no-reduce-accumulator-copy rejects non-spread accumulator copies inside reducers, complementing Oxlint's own oxc/no-accumulating-spread. Some are about evidence: no-chained-type-assertions rejects nested as and angle-bracket assertions, with the explicit carve-out that chains made only of as const remain valid. Some are about naming and shape: no-shape-in-symbol-names rejects the case-insensitive substring shape in locally owned symbol names, but permits static member names such as Zod's schema.shape because those cannot be renamed locally.
Two design decisions are worth calling out because they show the author thought about false positives. no-conditional-empty-object-spread reports object spreads that use a conditional {} branch to omit fields, and the README states it intentionally has no autofix because omission is not equivalent to assigning undefined. no-known-value-widening allows empty dictionary accumulators and finite-key Record targets, so the rule is not a blanket ban on dictionaries, only on widening a known value into an open one.
The Effect rules ship as a second plugin, anti-slop-effect, registered separately. The README explains why: projects that do not use Effect should not inherit Effect architecture policy. That is a clean separation and the right call for a ruleset this opinionated.
Installing anti-slop with the agent skill, then a first lint run
The README offers two paths. The skill path is the intended one. It copies the plugin, installs compatible Oxlint dependencies, matching an existing Oxlint version when one is present, merges the plugin into the existing lint configuration, enables every generic rule, and validates the result. In repositories that depend directly on Effect, it also enables the opt-in Effect rule group.
Start by listing what the skill bundle contains, so you know what will be written before anything is written:
npx skills add dmmulroy/anti-slop --listThen add the install skill and ask your coding agent to install or configure anti-slop in the current repository:
npx skills add dmmulroy/anti-slop --skill install-anti-slopThe manual path is a copy of src/ into the target repository, for example at tools/oxlint/anti-slop/. If the repository already uses oxlint, install @oxlint/plugins at exactly the resolved Oxlint version; otherwise install the same current version of both packages. The README is explicit that both versions should stay exact so upgrades move them together.
After copying, register the entry point in oxlint.config.ts. The README's example includes an ignorePatterns list covering agent tooling directories and the vendored plugin itself, then the plugin registration, then the rules:
import { defineConfig } from "oxlint";
export default defineConfig({
ignorePatterns: [".claude/**", ".cursor/**", "tools/oxlint/anti-slop/**"],
jsPlugins: [
{ name: "anti-slop", specifier: "./tools/oxlint/anti-slop/index.ts" },
],
rules: {
"anti-slop/no-chained-type-assertions": "error",
"anti-slop/no-unknown-parameters": "error",
"anti-slop/no-module-mocking": "error",
},
});The README shows the full rule list with every generic rule set to error; the snippet above is a subset for illustration. What you should see after the first run is a set of diagnostics on existing code, not a clean pass. Expect the type-assertion and unknown-related rules to fire first. The README notes the same ignorePatterns, jsPlugins and rules block works under lint in a Vite+ config, and that the ignore patterns should be merged into Vite+'s fmt.ignorePatterns as well so vp check does not reformat installed agent assets or the vendored plugin.
Updating a vendored copy without losing your edits
Vendoring creates an upgrade problem, and the README addresses it head-on. Ask your agent to update anti-slop while preserving local customizations, optionally naming an upstream revision or selected fixes. The skill stages incoming source separately, uses a three-way merge when the original upstream snapshot is recoverable, and otherwise ports reviewed changes conservatively. It preserves local rules and configuration, asks about conflicting policy and enabling new rules, and records provenance for future updates. It does not force-replace the vendored directory.
That last sentence matters. A force-replace would be simpler to implement and would silently discard the edits the README tells you to make. Staging plus a three-way merge is the more expensive design, and it only works if provenance was recorded on the previous update. If it was not, the fallback is manual porting of reviewed changes, which is slower and depends on a human reading a diff.
One caveat the README raises: an already-installed skill bundle may be older than upstream, and the copy script itself does not fetch or merge updates. For the latest upstream you have to ask the agent to retrieve and identify that revision. There is no release feed to poll, because there are no releases.
Where anti-slop is the wrong tool
The most obvious failure mode is a codebase that leans on Vitest or Jest module mocking. no-module-mocking rejects mock, doMock and unstable_mockModule calls in favor of real dependency seams. If your test suite is built around module mocking, enabling this rule is not a lint cleanup, it is a test refactor. The same applies to no-runtime-typeof if your boundary parsing is ad hoc typeof narrowing scattered through application code rather than concentrated at the edges.
A second limitation is the dependency model itself. There is no official npm package, and package.json marks the project private. Anyone who wants a pinned version, a changelog and a semver contract is asking for something this project deliberately does not provide. The README acknowledges community-maintained forks and packages are welcome, but states their compatibility and release lifecycle belong to their maintainers. That is an honest boundary, and it also means the maintenance burden lands on you.
A third case: teams that use Effect but do not want Effect architecture policy enforced. The Effect rules are opt-in and live in a separate plugin, so this is avoidable, but it is avoidable only if you leave anti-slop-effect unregistered. The generic ruleset is already opinionated enough that a team without a shared position on type assertions and unknown handling will spend more time arguing about rule configuration than writing code. The README's own framing supports this: if you want a universal coding standard, this is not it.
Compared with the built-in oxc rules and typescript-eslint
The closest comparison is Oxlint's own oxc rule group. Anti-slop does not replace it; the README's config enables oxc/no-accumulating-spread alongside the anti-slop rules, and no-reduce-accumulator-copy is described as complementing that native rule. So the built-in group covers general correctness and performance patterns, while anti-slop adds the evidence-discipline rules: assertions, unknown surfaces, Reflect usage, module mocking, dictionary widening. If you already run Oxlint, anti-slop is an addition to your config, not a migration.
The other comparison is typescript-eslint. That project is a general-purpose linting layer with a broad rule catalogue and a versioned package you install normally. Anti-slop takes the opposite approach on both axes: a narrow, opinionated rule set that you copy and edit rather than depend on. The practical difference is who owns the rules. With typescript-eslint, upstream owns them and you configure them. With anti-slop, you own them the moment you copy src/, and upstream only offers a merge path for future changes. That is a real trade-off, not a strict improvement. You get rules tuned to a specific standard of evidence, and you accept that you are now maintaining a plugin.
Licence and maintenance cost
The repository is licensed MIT, which permits copying, modification and redistribution provided the licence and copyright notice are preserved. Vendoring source into your own repository is squarely within what MIT allows. The README's guidance to read the rules and change them to match your team's standards is compatible with the licence, but if you redistribute the modified plugin, the MIT notice still has to travel with it. This is a general description of the licence, not legal advice; check the LICENSE file for the operative text.
The maintenance picture: the last push to the default branch was on 2026-09-10, six days before this writing, and the repository is not archived. package.json declares version 0.1.2 and marks the package private, which is consistent with the vendoring model. There is no release feed to follow, so upgrade decisions come from reading upstream commits or asking the skill to identify a revision.
Your recurring cost is the vendored directory. Every upstream change has to be merged against your local edits, and the three-way merge only helps when provenance was recorded previously. Budget for one person owning tools/oxlint/anti-slop/ the way they would own any other internal tool, including the decision about which new rules to enable.
Editorial conclusion
Adopt anti-slop if your team already runs Oxlint, writes TypeScript at boundaries, and is willing to own a vendored plugin whose rules you edit. Skip it if you need a versioned dependency with a release lifecycle, if you rely on Vitest module mocking, or if you use Effect and do not want Effect-specific policy enforced. Before adopting, run npx skills add dmmulroy/anti-slop --list to see what the skill would do, and read src/rules/no-known-value-widening.ts first, because that rule decides how much of your existing unknown-handling code survives the first lint run.
Frequently asked questions
What is the anti-slop Oxlint plugin?
It is a set of opinionated Oxlint rules that reject low-evidence and low-signal TypeScript and JavaScript patterns, distributed as source to be vendored rather than as an npm dependency. The README states it reflects the maintainer's preferences rather than a universal coding standard.
How do I install dmmulroy/anti-slop in my repository?
The README documents an agent skill: run npx skills add dmmulroy/anti-slop --skill install-anti-slop, then ask your coding agent to install or configure anti-slop in the current repository. The manual alternative is copying src/ into the repository and registering the entry point under jsPlugins in oxlint.config.ts.
Is there an official npm package for anti-slop?
No. The README states there is no official npm package and that the project is meant to be vendored, and package.json marks the package private. Community-maintained forks and packages are welcome, but the README says their compatibility and release lifecycle belong to their maintainers.
What does "AI slop" mean in slang?
The repository does not define the term; it uses "slop" to describe low-evidence and low-signal TypeScript and JavaScript patterns, such as chained type assertions or unknown parameters, that its rules reject. The README does not discuss AI-generated content.
Community notes