npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

stopslop

v1.2.0

Published

Static analysis for AI-generated JavaScript and TypeScript: find cognitive complexity, god classes/modules, duplicate code, dead code and orphan features.

Downloads

463

Readme

StopSlop

StopSlop clone-alert: 0 clones CI npm version

Stop AI-generated code from becoming instant legacy.

Coding agents are good at making code compile. They are also good at making too much of it: tangled functions, god files, copied blocks, dead exports, and entire features that pass their own tests but are never connected to the application.

StopSlop is a static analyzer for JavaScript and TypeScript codebases under heavy AI-agent change. It catches that structural overproduction before it becomes permanent. It complements ESLint, Biome, Oxlint, SonarQube, and SonarCloud rather than repeating their style and type rules.

npx stopslop .

It runs locally against JavaScript and TypeScript. No account. No source-code upload.

What it catches

  • Tangled functions using cognitive complexity from the SonarSource specification.
  • God classes and god modules that mix unrelated responsibilities or have grown into densely connected monoliths.
  • Useful split boundaries for god units when cohesion exposes responsibility groups that should move together.
  • Copy-pasted blocks, with optional identifier normalization to catch copies whose variables were renamed.
  • Dead exports, dependencies, and files.
  • Orphan features kept alive only by their own paired test but called by no production code.
  • Files exporting multiple classes, a common sign that generation kept appending instead of designing.

Complexity and structural analysis are implemented by StopSlop. Clone detection uses Clone Alert; dead-code analysis uses Knip. StopSlop detects code patterns, not whether a human or an AI wrote them.

Start with one command

Run without installing:

npx stopslop .

Or pin it in the project so local runs and CI use the same version:

npm install --save-dev stopslop
npx stopslop .

# pnpm
pnpm add -D stopslop
pnpm exec stopslop .

# Yarn
yarn add --dev stopslop
yarn stopslop .

The default report gives you the full repository score, the signal densities, and a grouped finding list. Add --details to show all clone locations and the suggested split boundaries for god units; use --json for every finding.

  stopslop  27 files  4.3 KLOC  980ms

  slop level: low (22.3/100)  ·  worse than 49% of reference repos
  god units 0.00/KLOC  ·  complex fns 4.42/KLOC  ·  duplication 0.8%
  baseline: 23 accepted · 0 new

  ✓ no new slop since baseline

Exit codes are made for CI: 0 means clear, 1 means findings, and 2 means the analysis itself failed.

Adopt an existing codebase without cleaning it first

You do not need a perfect repository to start. Record today's findings once, commit them, and fail only when a change adds an unaccepted finding:

# Record accepted legacy.
npx stopslop . \
  --baseline .stopslop-baseline.json \
  --update-baseline

git add .stopslop-baseline.json

# Use the same file as the CI gate.
npx stopslop . --baseline .stopslop-baseline.json

The baseline is a sorted, reviewable list of finding identities. Updating it is an explicit acceptance of debt, so baseline changes should be reviewed like source changes.

The baseline never lowers the score. These three surfaces answer different questions:

| Surface | What it tells you | | --- | --- | | Findings | What needs attention in this run | | Gate | Whether any finding remains unaccepted | | Score | How much slop the complete repository carries, including accepted legacy |

A baselined run therefore remains honest: the report can show moderate (43/100), 30 accepted, and 0 new while the gate is green.

Put the gate in your README

The badge is deliberately binary. It is a public CI status, not a miniature analytics report.

AI slop | clear       0 unaccepted findings
AI slop | detected    1 or more unaccepted findings

Generate the Shields endpoint JSON with the same baseline used by CI:

npx stopslop . \
  --baseline .stopslop-baseline.json \
  --format shields > stopslop-badge.json

Commit or publish stopslop-badge.json, then add this Markdown:

[![AI slop](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/OWNER/REPO/main/stopslop-badge.json)](https://github.com/BaryshevRS/stopslop)

Replace OWNER/REPO in the image URL with your repository. Keep the outer link unchanged: clicking the badge takes readers to StopSlop.

Without --baseline or --base, clear requires zero findings across the entire scan. With a baseline, it requires zero findings outside the committed baseline. The full score is still present in the terminal and JSON reports.

--format shields exits 0 for both clear and detected so a publishing step can write the current state. An incomplete or failed analysis exits 2 before emitting a badge.

Gate pull requests

Use a committed baseline for a stable, explicitly reviewed allowlist:

- name: Reject new AI slop
  run: npx stopslop . --baseline .stopslop-baseline.json

Or compare the working tree directly with the target branch and keep no baseline file:

npx stopslop . --base origin/main

--base analyzes both revisions and reports findings that exist only in the current tree. CI must check out enough Git history to resolve the base revision. The command never switches your branch or changes the index.

For GitHub Code Scanning, emit SARIF before propagating the gate failure:

npx stopslop . --base origin/main --format sarif > stopslop.sarif

Every rule in the SARIF report carries its help, so the alert says why the finding matters and how to fix it, not only what failed.

See CI and automation for a complete workflow that uploads SARIF and still preserves StopSlop's exit code.

Catch slop while the agent is still working

A check that first runs on the pull request reports code nobody remembers writing. StopSlop can run inside the coding session instead, while the code is still in front of the agent.

This repository is a plugin for Claude Code, Codex, and Cursor and an extension for Gemini CLI. Each agent gets the stopslop skill, which covers how to run the check, read the report, and fix each kind of finding without gaming it: no raised thresholds, no ignore globs, no baseline edits on the agent's own initiative. Where the agent supports it, the install also adds a stop hook.

| Agent | Install | Stop hook | | --- | --- | --- | | Claude Code | /plugin marketplace add BaryshevRS/stopslop/plugin install stopslop@stopslop | included | | Codex | codex plugin marketplace add BaryshevRS/stopslopcodex plugin add stopslop@stopslop | included; trust it when Codex asks at session start | | Cursor | Customize → import BaryshevRS/stopslop from a GitHub repository | included | | Gemini CLI | gemini extensions install https://github.com/BaryshevRS/stopslop | in project settings | | Others | npx skills add BaryshevRS/stopslop (skill only) | — |

Before the agent ends a turn that changed JS/TS source, the hook checks the uncommitted changes against HEAD, or against .stopslop-baseline.json when the project has one, and hands any new findings back with how to fix each kind. Legacy never blocks a turn, a turn that changed no source costs one git status, and the agent gets one nudge per turn: accepting a finding stays your decision. The hook runs the project's pinned stopslop from node_modules and falls back to npx stopslop@1.

The hook also ships in the npm package, so a project that pins stopslop can commit it to its own agent settings and give it to every contributor; see Commit the hook to the project.

Any agent or script. The JSON report is stable and versioned. Feed it into the next turn instead of paraphrasing terminal output:

npx stopslop . --base HEAD --json > slop-report.json

Each finding carries kind, file, line, and message, and rules says for each reported kind why it matters and what resolves it: the same text as docs/rules.md and the rule help in GitHub Code Scanning. God-unit findings include member groups that serve as concrete refactoring boundaries. Clone findings include every occurrence. Baseline and Git-base runs retain the complete score while returning only new findings.

Score: trend the repository, do not gate on the number

The 0..100 slop score normalizes findings by KLOC, so a repository can be compared with itself over time instead of merely growing a larger raw count. It combines four densities: god units, duplication, over-complex functions, and dead code.

Floors and budgets are anchored in a benchmark of 41 mature TypeScript repositories totaling 9.4 MLOC. The weights remain an explicit judgement call, not a scientific truth. That is why the binary gate uses findings rather than an arbitrary score cutoff.

The complete formula, calibration rationale, limitations, and corpus comparison are in docs/score.md and docs/thresholds.md.

CLI

stopslop [path]            analyze a directory or file (default: .)

  --format <fmt>           text (default) · json · sarif · shields
  --json                   alias for --format json
  --details, -d            show split groups, hubs, and all clone locations
  --fast                   skip clone and dead-code analysis
  --config <file>          use a specific stopslop.json
  --base <ref>             gate on findings absent from a Git revision
  --baseline <file>        gate on findings absent from a baseline file
  --update-baseline        replace the baseline with current findings
  --help, -h               show help

Invalid configuration, no supported source files, and hard analysis errors exit with code 2 before StopSlop emits a report, badge, or baseline.

Configuration

StopSlop works without configuration. To tune a check, add stopslop.json:

{
  "$schema": "https://unpkg.com/stopslop/schema.json",
  "cognitiveComplexity": 20,
  "duplicates": {
    "minTokens": 100,
    "minLines": 10,
    "ignoreIdentifiers": true
  },
  "ignore": ["**/*.generated.ts"]
}

Every field is optional. Set a check to false to disable it. The published schema provides editor completion and rejects unknown or invalid properties.

See docs/configuration.md for every field, default, and Knip workspace example. See stopslop.example.json for a complete configuration.

Dead-code configuration

Knip settings go under knip in stopslop.json. A project's own knip.json, .knip.json, knip.config.* and package.json#knip are never read, so an analysis does not change with whatever happens to sit in the directory. Everything in Knip's configuration reference works there unchanged.

You do not have to work out what belongs there. Run the analysis and Knip reports what its configuration is missing — which workspace it never reached, which declared entry point resolved to nothing — under config hints, with the file count each one accounts for:

  config hints  (from Knip)
  packages/core  ·  12 files
    Add entry and/or refine project files in workspaces["packages/core"] (12 unused files)
  → Knip settings live under `knip` in stopslop.json (knip.json is not read)
    reference: https://knip.dev/reference/configuration

Address them, run again, repeat until they are gone. Until then dead code is reported but left out of the score, because a run without entry points reports live files as unreachable. The same list is in configHints of --format json.

Node.js API

StopSlop ships ESM JavaScript and TypeScript declarations for programmatic use:

import { analyze, loadConfig } from 'stopslop';

const root = process.cwd();
const result = await analyze(root, loadConfig(root));

console.log(result.slop.score, result.findings);

The API also exports the resolved defaults, scoring helpers, baseline helpers, and report types. See the Node.js API guide for Git-base analysis, typed configuration, result semantics, and custom analyzers. The API is experimental and may change in a minor release; see COMPATIBILITY.md.

Design and limitations

  • StopSlop supports .js, .jsx, .mjs, .cjs, .ts, and .tsx source.
  • Tests, generated code, build output, dependencies, and common framework output directories are excluded by default.
  • A high score does not prove poor engineering, and a low score does not prove correctness. StopSlop measures structural overproduction, not total quality.
  • Dead-code accuracy depends on Knip knowing the project's entry points; the run says so through config hints when it does not.
  • A baseline accepts finding identities. If an accepted function becomes more complex without changing identity, the score rises but the gate stays clear.

Architecture, research references, and the boundary between published methods and StopSlop heuristics are documented in ARCH.md.

FAQ

Is StopSlop an AI-generated code detector?

No. It does not guess who wrote a line of code. It finds structural waste that coding agents often produce—duplicate blocks, over-complex functions, god classes and modules, dead code, and orphan features—and reports the same issue when a human wrote it.

How is StopSlop different from ESLint, SonarQube, SonarCloud, or Knip?

ESLint and similar linters focus on local rules. SonarQube and SonarCloud are broad quality platforms. Knip specializes in unused code and dependencies. StopSlop is a local, zero-account AI code quality gate focused on structural overproduction. It combines several signals into one report and keeps accepted legacy visible through its score while blocking only new findings through a baseline or Git comparison. Knip and Clone Alert power two of those signals.

Can I add StopSlop to a legacy repository?

Yes. Generate .stopslop-baseline.json, review and commit it, then use that same file in CI. Existing findings remain in the score and report as accepted debt; only unaccepted findings fail the gate.

Does StopSlop support monorepos and CI code scanning?

Yes. Configure Knip workspaces under stopslop.json#knip.workspaces for accurate dead-code analysis in a monorepo. Use SARIF for GitHub Code Scanning, JSON for automation and coding agents, or the Shields format for a README status badge.

Development

pnpm install
pnpm test
pnpm typecheck
pnpm check:clones
pnpm build
pnpm test:package
pnpm release:check

MIT licensed. See LICENSE.