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

slopslint

v0.3.2

Published

Deterministic repository gate for duplication, orphan surfaces, contract claims, tombstones, and monotonic ceilings.

Readme

slopslint

A deterministic slop gate for repositories where a lot of the code is written by agents. It asks two independent questions: is this repeated? and who uses this? Duplication and orphan findings are measured in configured scopes, specific debt can be accepted as a permanent reviewable tombstone, and each total is held under a committed ceiling that can only ever go down. An optional claims map also proves that every declared public surface is tied to a live contract item.

One self-contained binary. No runtime, no package manager, no separately installed detector.

slopslint check --classify --enforce

Why

Agent-written code duplicates. Not dramatically, and rarely in a way any single review catches: a helper re-derived in a second module, a setup block pasted into a fourth test, a rejection path written four times with one word changed. Each instance is defensible. The aggregate is not. Agent-written repositories also accumulate plausible-looking surfaces that nobody owns: for example, a generated copy whose only inbound references are its generator and its own guard test. A duplication-only gate cannot see that directory because nothing inside it needs to be repeated.

A percentage threshold does not hold that line, because it drifts up quietly as the repository grows. slopslint holds absolute counts per scope, and each committed count is monotonic: a change may lower it, never raise it. Debt you decide to keep is not silently subtracted — it is written down as a tombstone that names the incident, the rule it established, and the evidence.

Install

Binary (recommended — nothing else needed):

curl -fsSL https://github.com/thellmwhisperer/slopslint/releases/latest/download/slopslint-linux-x64 \
  -o /usr/local/bin/slopslint && chmod +x /usr/local/bin/slopslint

Published for darwin-arm64, darwin-x64, linux-arm64, linux-x64, and windows-x64.

GitHub Action:

- uses: thellmwhisperer/[email protected]
  with:
    args: check --classify --enforce

npm (needs Node 20+):

npx slopslint check --classify --enforce

Configure

The base duplication setup uses two files under .slop/, both yours to own and commit. Orphan and claims checks are off unless their blocks are present.

.slop/config.yml declares what to measure. Scope names, paths, and globs are your data — slopslint ships no default layout.

schema: 1

detector:
  name: jscpd
  version: "4.2.5"   # must match the detector linked into the binary

defaults:
  format: python
  mode: mild
  min_lines: 5
  min_tokens: 50

global_ignore:
  - "**/__pycache__/**"
  - "**/node_modules/**"

scopes:
  python_production:
    scan_path: py
    pattern: "**/*.py"
    ignore:
      - "**/test_*.py"
      - "**/conftest.py"

  python_tests_fixtures:
    scan_path: py
    pattern: "**/{test_*,conftest}.py"

# Optional. Every list is repository-relative and uses fast-glob syntax.
orphan_scopes:
  tools:
    files: ["tools/**/*.{ts,js}"]
    directories: ["skills/*"]
    exported_symbols: ["src/**/*.{ts,js}"]
    test_files: ["test/**/*.{ts,js}"]
    generation_files: ["scripts/generate*.{ts,js}"]
    ignore: ["**/fixtures/**"]

# Optional. Surface groups are labels, not language-specific concepts.
claims:
  file: .slop/claims.yml
  surfaces:
    commands:
      files: ["bin/*"]
    tools:
      directories: ["tools/*"]
    assets:
      files: ["assets/**/*"]
    api:
      exported_symbols: ["src/index.ts"]

Scopes are measured independently and never leak into each other, so duplication in production code cannot hide inside the test scope. Globs match basenames: a directory named test_data/ keeps its production files in the production scope.

An orphan scope enumerates exactly the configured files, directories, and JavaScript/TypeScript exports. The census then searches repository text for deterministic inbound evidence: static or dynamic imports, require calls, repository-relative path strings (including build files, CI, and docs). In Markdown, dot-relative paths such as ../src/used.ts are resolved from the document's directory. Embed/generate directives that name the path also count. A surface is an orphan when it has no inbound consumer outside itself, configured test files, or configured generation files. Those ignored references are included in the report with their reason. slopslint does not execute code, inspect runtime telemetry, or ask a model to infer ownership.

.slop/ceilings.yml declares what you will tolerate:

schema: 1
scopes:
  python_production:
    active_clones_ceiling: 21
  python_tests_fixtures:
    active_clones_ceiling: 110
orphan_scopes:
  tools:
    active_orphans_ceiling: 3

The ceiling is not a budget to spend. check --enforce fails when the measured count is above it (a regression) and when it is below it (an improvement you did not record). The same rule applies to active_orphans_ceiling. Paying debt down means lowering the number in the same change, so the base branch always states the truth.

Claims

When claims is configured, its committed YAML file maps each censused surface to a contract file and an opaque item string:

schema: 1
claims:
  bin/slopslint:
    contract:
      file: docs/contracts.md
      item: CLI-SLOPSLINT
  src/index.ts#runCheck:
    contract:
      file: docs/contracts.md
      item: API-RUN-CHECK

File and directory identities are their repository-relative paths. Exported symbol identities are path#symbol. The check fails when a censused surface is missing, a claim names a surface outside the census, the referenced contract file is absent, or that file no longer contains the item string. slopslint does not interpret the item or parse a contract language; choosing the relationship is an ordinary human- or agent-authored commit.

Commands

| Command | What it does | Exit | | --- | --- | --- | | slopslint check | run every configured census and claims sync; print canonical JSON | 0 | | slopslint check --classify | additionally split accepted from active | 0 | | slopslint check --classify --enforce | additionally fail on ceiling violations and stale records | 0 / 1 | | slopslint ratchet <base-ref> | verify the committed ceilings only went down | 0 / 1 | | slopslint tombstone list \| check \| add | manage .slop/tombstones | 0 / 2 |

Exit 1 is a gate condition. Exit 2 is invalid usage or an invalid record.

Tombstones

A tombstone is a permanent, reviewable record of debt you decided to keep. It CONSUMES a finding; it never produces one.

schema: 1
id: T-SHARED-FIXTURE-HEADER
status: accepted            # accepted | legacy
category: duplication       # duplication | orphan | alien_code | debt_normalization
title: "Fixture header shared by the ingest suites"
created_at: 2026-08-04
incident:
  pattern: >
    What the smell is, in general terms.
  what_went_wrong: >
    What actually happened here.
  root_cause: >
    Why it got through.
  rule_established: >
    What the team does differently now.
  evidence:
    - example: "the concrete instance"
      family: clone_fingerprint
match:
  family: clone_fingerprint
  scope: python_tests_fixtures
  fingerprint: <64 hex chars from `slopslint check`>

Accepted clones and orphans stop counting as active, so ceilings measure live debt only. Orphans use the same record shape with category: orphan, family: orphan_fingerprint, scope, and the finding's SHA-256 fingerprint. Two rules keep that honest:

  • a record whose fingerprint matches nothing is stale and fails the gate — debt you paid off cannot leave its exemption behind;
  • categories with no detector (alien_code, debt_normalization) are standing records: validated, reported, never matched, never stale. They carry the incident now and reserve the schema path for a detector later. Because nothing matches them, several standing records may share family, artifact and category (one per incident); their ids must still be unique.

Standing records use one of the reserved families, which have no detector yet:

  • agent_artifact_in_repo
  • documented_as_convention
  • environment_layout_coupling
  • format_churn: formatting changes to lines the change did not otherwise touch, with no rule in the repository requiring them
  • inline_foreign_language
  • mock_heavy_test: a test that replaces the code under test's own internals with mocks or monkeypatches instead of driving it through its inputs
  • runtime_dependency
  • self_validating_test: a test whose fixture encodes the author's assumption about an external tool or format, so it passes against behaviour that does not exist
  • speculative_feature: an option, flag, parameter or code path with no caller or user that asked for it
  • speculative_hardening: code and tests added to guard against a failure the design already rules out (random suffixes on names unique by construction, retries around calls that cannot fail transiently, validation of values the caller produces itself)
  • subprocess_foreign_interpreter
  • test_weakening: a test an issue or contract asks for is deleted, skipped or loosened

Check output

With only duplication configured, output remains the original array (or the original classified object), byte for byte. Configuring orphans or claims emits an object with the existing scopes plus opt-in keys:

{
  "scopes": [],
  "orphan_scopes": [
    {
      "scope": "tools",
      "count": 1,
      "orphans": [
        {
          "scope": "tools",
          "kind": "file",
          "path": "tools/generated.ts",
          "fingerprint": "<64 lowercase hex characters>",
          "evidence": [
            {"source": "test/generated.test.ts", "reference": "import", "reason": "test"},
            {"source": "scripts/generate.ts", "reference": "path", "reason": "generation"}
          ]
        }
      ]
    }
  ],
  "claims": {
    "count": 1,
    "surfaces": [
      {
        "scope": "commands",
        "kind": "file",
        "surface": "bin/slopslint",
        "contract": {"file": "docs/contracts.md", "item": "CLI-SLOPSLINT"}
      }
    ]
  }
}

The normative JSON Schema is docs/check-output.schema.json.

What fails closed

The gate refuses to pass rather than guess. Missing or invalid config, a detector version that disagrees with the linked library, zero scanned files, a clone path that escapes the repository, a range that runs backwards, a duplicated or ambiguous YAML key, a tombstone citing a file that does not exist, two records claiming one finding, a missing claim, a stale contract item, an orphan ceiling scope mismatch, or an unreachable git base ref — each is an error, never a silent pass.

Output carries integer totals, repository-relative paths, and a synthetic fingerprint per clone or orphan. It never carries source text, timestamps, floating percentages, or absolute paths, so a report is safe to paste into an issue. Two runs over one unchanged tree produce byte-identical output.

Development

bun install
bun test
bun run typecheck
bun run build:binaries

License

MIT