stop-ai-slop

mcp
Guvenlik Denetimi
Uyari
Health Uyari
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 5 GitHub stars
Code Uyari
  • fs module — File system access in .github/workflows/publish.yml
Permissions Gecti
  • Permissions — No dangerous permissions requested

Bu listing icin henuz AI raporu yok.

SUMMARY

Zero-dependency linter and gate that blocks AI-generated comment slop: multi-line narrative comments, changelog markers, step-numbered filler. Write-time plugin for OpenCode, Claude Code hooks, pre-commit, GitHub Action, GitLab CI, MCP server. RU+EN+DE+FR+ES detection.

README.md

stop-ai-slop

English | Русский

npm version
license
node
zero-deps

stop-ai-slop is a zero-dependency comment linter and gate that blocks AI-generated comment slop before it lands in your codebase. One scanner (skill/scripts/scan.mjs, the RULES table) is the single source of truth for a one-line, why-only comment policy. It is enforced at write-time (OpenCode plugin, Claude Code PreToolUse hook), at commit-time (pre-commit hook installed with --install), and in CI (GitHub Action, GitLab CI template) — and it also runs as an MCP server. Node >= 18, zero npm dependencies, MIT licensed, works on Windows, Linux and macOS.

AI coding agents over-comment: multi-line narrative blocks, // was X, now Y changelog notes, Step 1 / Step 2 filler, banner dividers, markdown inside comments, TODO without a ticket, comments that restate the code line beneath them, rotting file.py:123 pointers, even invisible zero-width and BiDi characters. stop-ai-slop catches all of it deterministically — no LLM, no scoring threshold, no network — and blocks the offending edit or commit.

Install in two commands

npm i -D stop-ai-slop
npx stop-ai-slop --install        # writes the pre-commit hook + npm scripts into the current repo

--install is the only command that modifies your repo: it appends a marked block to .git/hooks/pre-commit (idempotent, never clobbers an existing hook) and adds the stop-ai-slop / stop-ai-slop:all npm scripts. The hook embeds the absolute path to the scanner as of install time — after moving or re-cloning the scanner, run --install again.

Homebrew

brew tap WhiteBite/stop-ai-slop https://github.com/WhiteBite/stop-ai-slop
brew install WhiteBite/stop-ai-slop/stop-ai-slop

The formula wraps the npm package (this repository doubles as its own tap — the formula lives in Formula/stop-ai-slop.rb) and pulls in Homebrew's node, which satisfies the Node >= 18 requirement.

Who is it for

Teams and solo developers whose code is written partly or mostly by AI coding agents (OpenCode, Claude Code, Cursor, Codex, Copilot, Gemini CLI) and who want a comment policy enforced mechanically — at the write, at the commit, in CI — instead of relying on review discipline.

Why stop-ai-slop

  • Zero dependencies. The runtime is a single .mjs scanner (the self-test lives in a separate module, selftest.mjs, so the shipped gate stays lean); the OpenCode plugin is a single .ts file. No biome, ruff, oxlint, Python or ripgrep to install.
  • Blocks at the moment of the edit, not after. The OpenCode plugin rejects write/edit/multiedit; the Claude Code PreToolUse hook denies Write/Edit before they happen. Most alternatives only scan after the fact, or ask the model to run grep itself.
  • Multi-language natural-language detection. Changelog markers, numbered steps and "This function…" openers are matched in RU + EN + DE + FR + ES. Deterministic competitors are English-only; the LLM-based ones read any language but need an API key.
  • One source of truth. Every rule lives in a single RULES table; --explain <rule-id> prints each rule's rationale (Why / Instead of / Write / Ignore it when).
  • Machine-readable output. text (default), Reviewdog rdjson, and SARIF 2.1.0 for GitHub code scanning.
  • Legacy-friendly. A baseline grandfathers existing findings so the gate only fires on new slop.
  • Wide enforcement surface. OpenCode, Claude Code, Cursor, Codex, GitHub Actions, GitLab CI, MCP, VS Code, IntelliJ IDEA.

What you get, and what installs automatically

One scanner (skill/scripts/scan.mjs, the RULES table) is exposed through eleven surfaces. They are independent: enable the ones you need, none conflict, and all enforce the same rules.

Surface What it does How it gets installed
CLI (stop-ai-slop bin) scans files, staged changes or a diff; --explain, --audit, baseline automatic with npm i -D stop-ai-slop
pre-commit hook + npm scripts blocks every commit that adds slop automatic: npx stop-ai-slop --install
OpenCode write-time plugin rejects write/edit/multiedit at the moment of the write; shape-gates unknown tool names with write-shaped payloads manual: a one-line stub file (below)
Claude Code plugin skill + PreToolUse hook (blocks Write/Edit) + PostToolUse hook (delivers findings for the written file back to the model via stderr, exit 2) automatic: /plugin marketplace add + /plugin install
Agent skill (skill/SKILL.md) teaches the policy and the commands to any agent automatic with the Claude Code plugin; a manual junction/symlink for OpenCode
MCP server (--mcp) pull-mode slop_scan / slop_explain / slop_baseline for any MCP client manual: one config entry per client
GitHub Action / GitLab CI template blocks PR/MR pipelines on the diff manual: a workflow / CI snippet
VS Code task / IDEA File Watcher on-demand or on-save scan inside the IDE manual: a snippet / template import
--install-hooks writes agent hook configs (.codex/hooks.json, .github/hooks/stop-ai-slop.json, .devin/hooks.v1.json) + prints settings snippets for Gemini CLI / Qwen Code; shape-gates UNKNOWN tool names manual: npx stop-ai-slop --install-hooks
--install-rules generates agent instruction files from the RULES table (.cursor/rules/stop-ai-slop.mdc, .windsurfrules, CONVENTIONS.md, .clinerules, .devin/rules/stop-ai-slop.md, marked block in .github/copilot-instructions.md) manual: npx stop-ai-slop --install-rules
Config JSON Schema (schema/stop-ai-slop.schema.json) draft-07 schema for .stop-ai-slop.yaml with IDE autocomplete and parity enforcement shipped in npm tarball; modeline at top of config file

Nothing happens silently

npm i -D stop-ai-slop has no postinstall script: it only places files under node_modules. No hook, editor config or agent setting is touched until you run --install or add one of the snippets below yourself.

Which surfaces to pick

  • Git-only teams — the two commands above; done.
  • OpenCode users — add the plugin stub so slop is rejected at write time: create ~/.config/opencode/plugins/comment-gate.ts with one line, export { default } from "<path to node_modules>/stop-ai-slop/plugin/comment-gate.ts" (OpenCode 1.18.29+ and 2.x; on older 1.x use export { CommentGate } from ... instead).
  • Claude Code users — /plugin marketplace add WhiteBite/stop-ai-slop then /plugin install stop-ai-slop; the skill and both hooks arrive automatically.
  • Any MCP client (Cursor, Codex, others) — add the stdio entry npx stop-ai-slop --mcp (see "MCP server").
  • CI — add the GitHub Action or the GitLab include (see "IDE and CI").
  • Codex CLI / VS Code Copilot / Devin users — npx stop-ai-slop --install-hooks writes .codex/hooks.json, .github/hooks/stop-ai-slop.json, .devin/hooks.v1.json; shape-gating keeps it safe for unknown tool names.
  • Cursor / Windsurf / Cline / Aider users — npx stop-ai-slop --install-rules generates instruction files from the RULES table; see "Agent hook integrations".
  • Gemini CLI / Qwen Code — --install-hooks prints ready-to-paste settings snippets into .gemini/settings.json and .qwen/settings.json.

What it enforces, and why

Policy: a comment is at most one line and only states a non-obvious external constraint, an invariant, or a workaround. The retelling of a diff belongs in the commit message; the "why" of a test belongs in the test name. The rule table and the detector live in skill/scripts/scan.mjs (the RULES const) — change rules there; everything else only applies them.

Error rules block (exit 1; the write-time gate throws). Warning rules teach: they are printed but do not block (unless --strict).

Enforcement points

  1. OpenCode write-time plugin — plugin/comment-gate.ts intercepts write/edit/multiedit and rejects an edit that has error findings at the moment of the write; unknown or capitalized tool names whose payload is write-shaped (file_path+content, file_path+old_string+new_string, file_path+edits[], or patch text with *** Begin Patch) are evaluated by shape too, and a mutating-shaped call that cannot be evaluated is recorded in the audit log with verdict unevaluated. It applies the .stop-ai-slop.yaml of the edited file's repository root (resolved from the file, not process.cwd(), so a file in repo B is judged by repo B's policy even when OpenCode runs from repo A). Mounted into ~/.config/opencode/plugins/ via a re-export stub. The default export { id: "stop-ai-slop", server: CommentGate, setup } serves both APIs: OpenCode 1.18.29+ calls server() (v1 hook tool.execute.before), OpenCode 2.x calls setup() (registers ctx.tool.hook("execute.before"), the only V2 hook that may reject a call); the legacy named export CommentGate keeps old stubs on older 1.x working. OpenCode displays local plugins by their stub file name — name the stub stop-ai-slop.ts instead of comment-gate.ts if you want that label.
  2. Pre-commit hook via --install — one command weaves node .../scan.mjs --staged into .git/hooks/pre-commit (idempotent: appends a marked block without clobbering an existing hook) and adds the stop-ai-slop / stop-ai-slop:all npm scripts to package.json. The hook and npm scripts embed the absolute path to the scanner as of install time — after moving or re-cloning the scanner, run --install again.
  3. Agent skill — skill/SKILL.md (name: stop-ai-slop): the policy, the rule table, the run modes. Mounts into OpenCode and Claude Code.
  4. Baseline for legacy — 1) --install, 2) --baseline-write (records current findings), 3) commit the baseline, 4) from then on the gate sees only new findings. Baseline v2 stores each finding as a pair of lines — relpath:line plus fp:<hash>, the fingerprint being a SHA-256 hash (first 16 hex chars) of the rule id and the trimmed comment text — and matches by fingerprint, not by position: edits above a baselined line no longer resurrect legacy, while changed comment text surfaces as new slop. The same text pasted again is masked only up to the number of baselined occurrences, so a fresh copy of legacy slop still counts as new slop. Old v1 baselines (relpath:line lines only) keep masking by position until the next --baseline-write. --baseline-prune removes entries that have no live finding (in v2 the pair dies together); a repeated --baseline-write would also amnesty new slop — do not do that.

Quick start

git clone https://github.com/WhiteBite/stop-ai-slop && cd stop-ai-slop
node skill/scripts/scan.mjs --self-test       # detector sabotage test
node skill/scripts/scan.mjs scan .            # full scan: git-tracked + untracked non-ignored files, generated/binary/artifacts skipped
node skill/scripts/scan.mjs --staged          # only lines added in git diff --cached
node skill/scripts/scan.mjs --diff <ref>      # added lines of tracked files relative to <ref>; untracked files are invisible
node skill/scripts/scan.mjs --fix --dry-run   # preview the mechanical fixes as a unified diff, change nothing
node skill/scripts/scan.mjs --fix             # apply the mechanical fixes, then rescan and report what is left
node skill/scripts/scan.mjs --strict          # warnings also block the gate (exit 1)
node skill/scripts/scan.mjs --install         # npm scripts + pre-commit hook in the current repo
node skill/scripts/scan.mjs --install --strict  # same, but the hook runs --strict
node skill/scripts/scan.mjs --install-hooks     # write agent hook configs (Codex CLI, VS Code Copilot local hooks, Devin CLI) + print settings snippets for Gemini CLI / Qwen Code
node skill/scripts/scan.mjs --install-rules     # generate agent instruction files from the RULES table (.cursor/rules/.windsurfrules/CONVENTIONS.md/.clinerules/.devin/rules/, marked block in .github/copilot-instructions.md)
node skill/scripts/scan.mjs --baseline-write    # record current findings into the baseline
node skill/scripts/scan.mjs --baseline-prune    # remove baseline entries with no live finding
node skill/scripts/scan.mjs --doctor            # environment diagnostics: node, git, pre-commit hook path, config, baseline
node skill/scripts/scan.mjs --help              # help for all flags

Suppression directives: // stop-ai-slop-ignore-next-line [rule-id] (next line), // stop-ai-slop-ignore-line [rule-id] (current line), // stop-ai-slop-ignore-file (whole file); after -- give a reason. Directives are honored only in real comment segments — the same text inside a string literal does not suppress anything.

Exit 1 — there are error findings outside the baseline; otherwise 0. Exit 2 — a usage or git error (bad flag, nonexistent ref).

Output formats

By default — human-readable text with a final slop-gate: … line. The --format <text|json|sarif> flag (modes scan, --staged, --diff) switches stdout to a machine-readable format: only JSON is printed, the summary line is omitted. Exit codes do not depend on the format — still 0/1/2.

--format json — Reviewdog RDFormat: a single JSON object on one line (pipe-friendly), diagnostics sorted by path and line, an empty result is diagnostics: []:

npx stop-ai-slop --diff origin/main --format json | reviewdog -f=rdjson -reporter=github-pr-review

--format sarif — SARIF 2.1.0 with two-space indent; the rules array lists every rule regardless of findings, results only the real findings. Upload to GitHub code scanning:

- run: npx stop-ai-slop --diff origin/${{ github.base_ref }} --format sarif > results.sarif
- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: results.sarif

Output language

Messages are Russian by default; --lang en switches findings, --explain, --help, error messages, --fix, bench and audit output to English. Without the flag, the language is auto-detected: STOP_AI_SLOP_LANG > LC_ALL/LANG (anything not starting with ru selects English; unset — Russian). Rule ids and the instead: labels are English in both languages. The --install/--install-hooks/--install-rules output and the generated hook text remain Russian in this version.

Autofix (--fix)

--fix applies the deterministic, mechanical fixes across the whole scan scope in one pass — no agent, no LLM, no per-finding rewrite. Preview first, then apply:

node skill/scripts/scan.mjs --fix --dry-run   # print a unified diff of every planned change, touch nothing
node skill/scripts/scan.mjs --fix             # write the changes, then rescan and report what is left

--dry-run never writes: it prints a per-file unified diff (context 2) and a summary line запланировано N правок в M файлах; не чинится автоматически: K, exit 0. Apply mode writes the files, then rescans and prints the surviving findings through the normal text pipeline (exit 1 if an error finding remains outside the baseline, so it chains into CI). --strict also treats surviving warnings as a failure. The fixer is idempotent: a second --fix is a no-op.

Fixed mechanically (deletion or in-place shrink — "delete" is always an acceptable outcome per the policy):

Rule Fix
multi-line-comment, vend/file-summary-header delete the whole comment run
vend/section-divider delete the divider line (or strip it from an inline comment)
changelog-marker, vend/cross-file-ref, vend/obvious-comment delete a full-line comment; strip the trailing comment off a code line
vend/step-numbered strip the numbered-step prefix, keep the rest of the text (full-line and trailing inline comments)
vend/zero-width-chars, vend/bidi-controls remove the real invisible/BiDi characters

Not auto-fixed — they need a human or an agent to write a replacement, so --fix leaves them and reports them: long-comment (compress the meaning), vend/this-function-opener (rephrase as an invariant), vend/generic-todo (add a ticket), vend/markdown-in-comment (semantic), vend/cjk-noise (rewrite the identifier).

Safety rails: a block-comment opener is never deleted mid-block (only whole runs go); a real invisible character is stripped but its backslash-escape spelling in source (a BOM test literal) is left intact — that is the subject of the code, not slop; a legit emoji ZWJ sequence and a leading BOM are preserved; a stop-ai-slop-ignore-next-line directive is dropped together with the code it guarded only when that target line is itself deleted, so no directive is left dangling; suppression directives and the findings they cover are never touched. Generated, binary and git-ignored files are skipped exactly as in the scan.

For bulk semantic cleanup (the not-auto-fixed rules), the intended flow is: --fix first to clear the mechanical majority, then an agent pass over the residual report — or --baseline-write to grandfather what the team decides to keep.

Configuration

An optional .stop-ai-slop.yaml in the repository root (same place as the baseline: the git root, or the scan directory outside a repo). Read by the scan, --staged, --diff and --pre-tool modes and by the OpenCode write-time plugin, which resolves it from the edited file's git root (falling back to the file's directory outside a repo). The parser is a zero-dep YAML subset: key: value scalars, lists via - , a rules: section with two-space indent, an overrides: list of {paths, rules} entries, # comments and blank lines skipped, values may be quoted. Unknown keys are ignored; an invalid severity or an unknown rule id in rules/overrides is exit 2 with the file name and line number.

Key Meaning
maxCommentLength comment-line length threshold for long-comment (default 120)
excludePaths list of relative path prefixes: a path is excluded if it equals an entry or starts with entry/; works in the full scan and in diff modes
generatedPaths list of relative path prefixes treated as generated (same prefix semantics as excludePaths); see Generated code
scanGenerated true disables the generated-file exemption — generated code is linted like any other file
rules severity override per rule id: error, warning or off (rule disabled)
overrides list of per-path severity overrides, each {paths: [patterns], rules: {rule-id: severity}}; a pattern without * is a path prefix (same semantics as excludePaths), * matches any characters except /, ** matches any characters including /; for a matching file the last matching entry wins, and an override beats the global rules remap
ticketPattern regex source enabling vend/ticket-ref (e.g. '\bKRY-\d+\b'); without the key the rule is inert — a bare ticket reference in a comment is flagged, while TODO lines, tracker links and CVE-/GHSA- advisories are exempt
maxCommentLength: 100
excludePaths:
  - generated
  - docs/api.md
rules:
  multi-line-comment: off
  vend/step-numbered: error
overrides:
  - paths:
      - "*.yaml"
      - ".github/**/*.yml"
    rules:
      multi-line-comment: warning

Severity remap is applied after detection and before baseline filtering and exit-code computation; per-path overrides are applied on top of the global rules remap — for a matching file the last matching entry wins, and off removes the finding. The baseline matches on the finding fingerprint (rule id + trimmed text; on rel:line for old v1 baselines) regardless of severity, so changing a severity in the config neither resurrects nor masks baselined findings.

Config JSON Schema

schema/stop-ai-slop.schema.json (draft-07, shipped in the npm tarball). Modeline at top of .stop-ai-slop.yaml:

# yaml-language-server: $schema=https://raw.githubusercontent.com/WhiteBite/stop-ai-slop/v2.0.0/schema/stop-ai-slop.schema.json

(or node_modules path ./node_modules/stop-ai-slop/schema/stop-ai-slop.schema.json). Parity with the parser is enforced by a self-test gate (schema-parity-config / schema-parity-rules).

MCP server

The --mcp flag runs stop-ai-slop as an MCP server over stdio (JSON-RPC 2.0). This is pull-mode: any MCP client calls the scanner itself before editing a file.

npx stop-ai-slop --mcp

The server supports protocols 2024-11-05, 2025-11-25 and 2026-07-28 — the version is negotiated on initialize. JSON-RPC batch requests (an array in) are answered with an array of responses. stdout carries only protocol messages; logs go to stderr. slop_scan resolves .stop-ai-slop.yaml and the baseline from the git root of the scanned path, not from the server's cwd.

Three tools:

Tool Arguments Description
slop_scan { path?: string } Full scan of a directory or file; returns text findings
slop_explain { ruleId: string } Returns a rule's rationale (Why / Instead of / Write / Ignore it when)
slop_baseline {} Prints the baseline entries (relpath:line and fp:<hash> lines)

Client setup:

Claude Code (.mcp.json):

{
  "mcpServers": {
    "stop-ai-slop": {
      "command": "npx",
      "args": ["stop-ai-slop", "--mcp"]
    }
  }
}

OpenCode / Cursor (stdio entry in the configuration):

{
  "mcpServers": {
    "stop-ai-slop": {
      "command": "npx",
      "args": ["stop-ai-slop", "--mcp"]
    }
  }
}

Blocking before the write (Claude Code)

The --pre-tool flag reads a PreToolUse JSON payload from stdin ({ tool_name, tool_input }), scans the proposed delta content (the Write content, or the Edit difference — new_string minus old_string), and on error findings prints them to stderr and exits with code 2 — Claude Code cancels the tool call and shows the reason to the model. A clean payload exits 0 with no output.

The plugin already carries the hook (hooks/hooks.json, matcher Write|Edit), so a marketplace install gets it automatically. For manual setup, add to .claude/settings.json:

{
  "hooks": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        {
          "type": "command",
          "command": "node \"<path to repo>/skill/scripts/scan.mjs\" --pre-tool"
        }
      ]
    }
  ]
}

The alternative decision form via hookSpecificOutput.permissionDecision (deny) exists, but this hook uses exit 2 + stderr to surface multi-line findings.

The plugin also ships a SessionStart hook (no matcher — startup, resume and clear) running scan.mjs --policy, which states the one-line comment policy and the per-rule summary up front in the session context, so most edits never trip the gate.

For agent-driven repair, the scan, --staged, --diff and --stdin-path modes accept --fix-suggestions: after the normal output the scanner prints one extra line, fix-suggestions: <single-line JSON array>, listing the exact edits --fix would apply — one {"file","line","rule","kind","from","to"} entry per mechanically fixable finding (kind is delete-line, replace-line or delete-run; findings that need a human rewrite, like long-comment, get no entry). With --format json or --format sarif the line goes to stderr, so stdout stays machine-parseable.

fix-suggestions: [{"file":"src/a.ts","line":3,"rule":"changelog-marker","kind":"delete-line","from":"// this fixes the cache miss","to":null}]

Agent hook integrations

--pre-tool generalization

Beyond Claude Code's Write/Edit/MultiEdit, --pre-tool now understands:

  • Gemini CLI / Qwen Code — tools write_file{file_path,content} and replace{file_path,old_string,new_string}.
  • OpenAI Codex CLI — apply_patch: tool_input.command carries a V4A patch (*** Begin Patch / *** Add File: / *** Update File: / *** Move to: / *** Delete File: / *** End Patch, +lines = added content); multi-file patches are scanned per file.
  • UNKNOWN tool names — gated by SHAPE: payload with file_path+content, or file_path+old_string+new_string, or file_path+edits[], or patch text containing *** Begin Patch. This covers VS Code Copilot local hooks (Preview feature) and Devin CLI whose tool names are not a stable API. Read-only tool names (containing read/view/grep/search/glob/list/ls/bash/shell/exec/run/fetch/web/think/todo/plan) never gate.

--install-hooks

Writes agent hook configs invoking node <abs>/scan.mjs --pre-tool:

  • .codex/hooks.json — Codex CLI, Claude-style envelope; matcher Write|Edit|MultiEdit|write_file|replace|apply_patch; merges with existing entries, never clobbers foreign hooks; broken JSON skipped with warning.
  • .github/hooks/stop-ai-slop.json — VS Code Copilot local hooks (Preview feature); own file rewritten each run; no matcher in that format — shape-gating keeps it safe; timeout 30s.
  • .devin/hooks.v1.json — Devin CLI, no matcher → all tools → shape-gating. Idempotent; refreshes the embedded scanner path in place.

Prints ready-to-paste snippets for Gemini CLI (.gemini/settings.json, hooks.BeforeTool, matcher write_file|replace, timeout ms) and Qwen Code (.qwen/settings.json, hooks.PreToolUse) — settings.json is user-global so it is never auto-edited.

--install-rules

Generates agent instruction files from the RULES table (single source of truth):

  • .cursor/rules/stop-ai-slop.mdc — Cursor; frontmatter description/globs/alwaysApply.
  • .windsurfrules — Windsurf.
  • CONVENTIONS.md — Aider; pair with --read CONVENTIONS.md.
  • .clinerules — Cline.
  • .devin/rules/stop-ai-slop.md — Devin.
  • Marked block inside .github/copilot-instructions.md — VS Code Copilot; foreign content preserved, block replaced in place.

Files owned by us are overwritten; shared-name files WITHOUT our first-line marker are never clobbered (skipped with a warning). Generated files pass our own scanner.

Tool coverage summary

Agent Integration
Claude Code PreToolUse exit 2 (see "Blocking before the write")
Codex CLI .codex/hooks.json (matcher `write_file
VS Code Copilot Local hooks (Preview), .github/hooks/stop-ai-slop.json
Devin CLI .devin/hooks.v1.json (also auto-reads .claude/ hooks)
Gemini CLI / Qwen Code Printed settings snippets (.gemini/settings.json / .qwen/settings.json)
OpenCode Write-time plugin (see "Enforcement points")
Cursor / Windsurf / Cline / Roo / Aider / Goose / OpenHands / Codex / Copilot AGENTS.md or their rules files → --install-rules + our AGENTS.md-compatible skill cover them
MCP clients (Cursor, Zed, JetBrains AI, Cody, …) Existing --mcp

IDE and CI

VS Code

The extension in editors/vscode/ is the primary path: copy or link that directory into ~/.vscode/extensions/whitebite.stop-ai-slop-0.1.0/, reload the window, and set stopAiSlop.command to your scanner invocation (default npx stop-ai-slop). Findings land in the Problems panel on save and via the stop-ai-slop: Scan the current file / Scan the workspace commands; see editors/vscode/README.md.

The zero-install alternative is a tasks.json task with a problem matcher to highlight findings in the Problems panel:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "stop-ai-slop scan",
      "type": "shell",
      "command": "npx stop-ai-slop scan",
      "problemMatcher": {
        "owner": "external",
        "source": "stop-ai-slop",
        "severity": "error",
        "fileLocation": ["relative", "${workspaceFolder}"],
        "pattern": {
          "regexp": "^(.+):(\\d+)\\s+(\\S+)\\s+\\[(error|warning)\\]\\s+(.+)$",
          "file": 1,
          "line": 2,
          "code": 3,
          "severity": 4,
          "message": 5
        }
      },
      "presentation": {
        "reveal": "always",
        "panel": "new"
      }
    }
  ]
}

One-line pattern: nested instead: lines simply do not match. Run from the terminal or bind to a hotkey.

GitLab CI

The templates/stop-ai-slop.gitlab-ci.yml template blocks MR pipelines on the diff (exit code); no code-quality report is shipped. Wire it in:

include:
  - project: 'WhiteBite/stop-ai-slop'
    file: '/templates/stop-ai-slop.gitlab-ci.yml'
    ref: <tag>

Bitbucket Pipelines

pipelines:
  pull-requests:
    "**":
      - step:
          image: node:20
          script:
            - npx stop-ai-slop --diff origin/$BITBUCKET_PR_DESTINATION_BRANCH

CI (GitHub Actions)

on: pull_request:
  jobs:
    slop:
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v4
        - uses: WhiteBite/stop-ai-slop@main
          with:
            base: ${{ github.base_ref }}

The action fetches the base ref itself, so a standard shallow checkout is enough; strict: "true" enables warnings-as-errors; format: "json" or format: "sarif" switches the action output to a machine-readable format (see "Output formats"). The action fails when no base ref is resolvable (a push event without the base input) instead of scanning an empty diff — use pull_request or pass base explicitly. The annotations input (default "true") adds --annotations to the text-format scan: every finding is also emitted as a GitHub Actions workflow command (::error file=<rel>,line=<n>::… / ::warning …) and shows up inline on the PR diff; with format: "json" or "sarif" the flag is a no-op.

Publishing to npm

Bootstrap, once: the first publish of a new package needs interactive confirmation — npm publish in a terminal: npm either asks for an OTP (if 2FA is on) or offers browser-approve ("Authenticate your account at …"), which is enough without an OTP and without 2FA; the third path is a granular token with bypass-2FA in ~/.npmrc. Right after it: npmjs.com → package Settings → Trusted publishing → add WhiteBite/stop-ai-slop and the publish.yml workflow; if that form requires enabling 2FA, that is the only place it is mandatory for fully automatic tags.

After that, deploy is tag-driven: bump the version in package.json + add a CHANGELOG entry, commit, git tag vX.Y.Z && git push origin main --tags. The .github/workflows/publish.yml workflow (trigger push: tags: v*) runs the self-test, skips the publish if that version is already in the registry (tag arrival order does not matter), and publishes via OIDC with provenance. Node 24 in the workflow is required: OIDC publishing needs npm CLI >= 11.5.1. After a release, update Formula/stop-ai-slop.rb: point url at the new version's npm tarball and recompute sha256 — the tarball hash changes with every version.

The same workflow also creates the GitHub Release for the tag (notes taken from the matching CHANGELOG.md section, skipped when the release already exists) and mirrors the package to GitHub Packages as @whitebite/stop-ai-slop (the GitHub npm registry only accepts scoped names; the mirror is skipped when that version is already there). Consumers of the mirror configure the registry per scope:

//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
@whitebite:registry=https://npm.pkg.github.com

Mounting on another machine

git clone https://github.com/WhiteBite/stop-ai-slop <path>
New-Item -ItemType Junction -Path "$env:USERPROFILE\.config\opencode\skills\stop-ai-slop" -Target "<path>\skill"
New-Item -ItemType Junction -Path "$env:USERPROFILE\.claude\skills\stop-ai-slop" -Target "<path>\skill"

OpenCode write-time plugin: a file %USERPROFILE%\.config\opencode\plugins\comment-gate.ts with the single line
export { default } from "<path>/plugin/comment-gate.ts". The default-export stub requires OpenCode 1.18.29 or newer (including 2.x); on older 1.x releases use the legacy one-liner export { CommentGate } from "<path>/plugin/comment-gate.ts".
The cmd.exe equivalent is mklink /J; on Linux/macOS use ln -s. In any git repo without agents, scan.mjs --install works.

Other integrations

pre-commit framework

repos:
  - repo: https://github.com/WhiteBite/stop-ai-slop
    rev: v2.0.0
    hooks:
      - id: stop-ai-slop

The hook runs --staged on every commit.

Claude Code / Cursor / Codex

The repository is a ready Claude Code plugin marketplace:

/plugin marketplace add WhiteBite/stop-ai-slop
/plugin install stop-ai-slop

The plugin carries the skill and a PostToolUse hook (Write|Edit → scan.mjs --stdin-path) that scans the just-written file and, when the gate fails, prints the findings to stderr and exits 2 — Claude Code delivers them back to the model. For Codex CLI, run npx stop-ai-slop --install-hooks to write .codex/hooks.json (see "Agent hook integrations") instead of copying the Claude hook by hand. Cursor has no public write-time hook surface yet — use npx stop-ai-slop --install-rules to drop the policy into .cursor/rules/.

IntelliJ IDEA

File Watchers (Ultimate)

The idea/filewatchers/stop-ai-slop.xml template imports via Settings → Tools → File Watchers → + → Import. After importing, replace <path-to-scan.mjs> with the absolute path to skill/scripts/scan.mjs on your machine. The template targets the common types (Kotlin, Java, TypeScript, JavaScript, Python, YAML); other scanner profiles are covered by External Tools or the CLI scan. Runs on every file change; scan exclusions are the standard artifact directories (venv, node_modules, .git, build, target, .next, out, Pods, site-packages, .dart_tool, .gradle).

Findings appear in the Run window with clickable paths, because the scanner output format is file:line.

Actions on Save (all IDEA 2024+ editions)

There is no built-in support for external commands. Two working options:

  1. External Tools (Settings → Tools → External Tools → +): program = node, arguments = <path>/scan.mjs scan $FilePath$, working directory = $FileDir$. Trigger manually or via the Save Actions plugin.
  2. File Watcher (see above) — the only on-save option without third-party plugins; available only in Ultimate.

Debugging

The OpenCode plugin writes every gate decision to a JSONL log (~/.config/opencode/logs/comment-gate.jsonl, path overridden by the STOP_AI_SLOP_LOG variable): loaded, blocked and passed events with the tool, file and rules. Inspect:

node skill/scripts/scan.mjs --audit        # counters + last 20 entries
node skill/scripts/scan.mjs --audit 50     # last 50

node skill/scripts/scan.mjs --doctor diagnoses the environment in one pass — node version, git on PATH, the pre-commit hook and its embedded scanner path (the moved-scanner case above), the npm scripts, .stop-ai-slop.yaml validity, the baseline and the OpenCode plugin stub — and exits 1 naming the broken piece; ! lines are informational and never fail.

The plugin is loaded by the OpenCode process at session start: after editing plugin/comment-gate.ts, restart OpenCode, otherwise the old version runs (the audit log shows this immediately by the absence of new entries).

Language profiles

Comment syntax comes from a language profile, not a single shared list: # is a comment in .py/.sh/.yaml but a preprocessor directive in .c and an attribute in .rs. 160 extensions and 26 file names are supported: Dockerfile, Containerfile, Makefile, GNUmakefile, Justfile, Rakefile, Vagrantfile, Gemfile, CMakeLists.txt, Jenkinsfile, BUILD, BUILD.bazel, WORKSPACE, WORKSPACE.bazel, meson.build, SConstruct, SConscript, Pipfile, Procfile, .env, .gitignore, .dockerignore, .npmignore, .gitattributes, .gitmodules, .editorconfig. Matching: exact file name → extension → name prefix, so suffixed variants (Dockerfile.dev, Makefile.am) are detected by prefix, while build.gradle stays c-family — a bare BUILD does not capture it.

Profile Line comment Block / doc Examples
c-family // /* */, /** */, {/* */} ts, js, kt, java, rs, cs, c, cpp, swift, dart, scala, mts, cts, sol, v, sv, qml, styl, res
go // /* */, /** */, {/* */} go (doc-comment convention: a // run above a declaration whose first word names it is exempt from multi-line/header)
css //, /* /* */ css, scss, less
py # """ / ''' py, pyi, vy
hash # — rb, sh, yaml, toml, ex, raku, awk, go.mod, go.sum, Dockerfile, Makefile, .gitignore
gherkin # — feature (Gherkin: multi-line and summary-header exempt; changelog/step/markdown still apply)
hashblock #, /* /* */ nix, hcl, tf, tfvars
powershell # <# #> ps1, psm1
julia # #= =# jl
nim # #[ ]# nim
sql --, # /* */ sql, plsql, pks, pkb
dash -- — vhd, vhdl, adb, ads
lua / haskell -- --[[ ]] / {- -} lua, hs, elm, purs, idr, agda, dhall
lisp ; — clj, el, scm, rkt
percent % — tex, bib, erl
fortran / vb / batch / vim ! / ', REM / ::, REM / " — f90, vb, bat, vim
rst .. — rst
markup <!-- <!-- --> html, xml, svg, md, xsl
mdx <!-- <!-- -->, {/* */} mdx
vue //, /*, <!-- /* */, {/* */}, <!-- --> vue, svelte, astro
ocaml (* (* *) ml, mli
php //, # /* */, /** */ php
pascal //, (* (* *), /// doc pas, pp, fs (F#)
coffee # ### ### coffee, litcoffee
adoc // //// //// adoc, asciidoc
handlebars {{!, <!-- {{!-- --}}, <!-- --> hbs
gotmpl {{/* {{/* */}} tpl, gotmpl, gohtml, tmpl
ini / properties ;, # / #, ! — ini, properties, .editorconfig

.m is not scanned: the extension is ambiguous (Objective-C vs MATLAB). .pp is also ambiguous (Puppet vs Pascal) — marked as pascal. Not scanned: COBOL, assembly (.asm/.s), .ahk, .ipynb, server-side engine templates (.erb, .ejs, .jsp, .cshtml, .razor, .twig, .blade.php, .pug, .haml). A new language is added with one row in the scan.mjs profile table.

Rules

Rule Severity What it catches
multi-line-comment error a comment spans 2+ consecutive lines (doc-blocks and /// doc-comment runs exempt)
changelog-marker error the comment retells the diff (a pair of changelog markers or a strong marker, ru/en/de/fr/es)
long-comment error a comment line longer than 120 characters
vend/step-numbered warning a numbered step in a comment (Step N / Шаг N / N., any language marker)
vend/section-divider warning a divider line made of -=#* characters
vend/markdown-in-comment warning markdown syntax inside a comment (**, -, |)
vend/this-function-opener warning a comment starts with "This function/…", «Эта функция/…», «Diese Funktion…», «Cette fonction…» or «Esta función…»
vend/file-summary-header warning a 2+ line summary-header comment at the top of a file
vend/generic-todo warning a TODO/FIXME/XXX without a ticket link (any case)
vend/ticket-ref warning a bare ticket reference in a comment — the change context belongs in the commit message or PR, not the code
vend/cross-file-ref warning a pointer to another file/line in a comment (handler.py:147)
vend/obvious-comment warning a single-line comment restates the code line beneath it
vend/ai-plan-narration warning a comment narrates the agent's own work process (plan/spec/task references, instruction acknowledgements)
vend/ai-vocab-density warning 3+ distinct AI-vocabulary words (delve, pivotal, tapestry...) in the file's comments
vend/research-citation warning a research citation in a comment — code is not a bibliography
vend/self-suppression warning a suppression directive without a rule list arrived together with the code it suppresses
vend/cjk-noise warning CJK characters glued to Latin letters or digits in the code part of a line (a generation artifact)
vend/zero-width-chars error an invisible zero-width character (U+200B, U+200C, U+200D, U+2060, U+FEFF)
vend/bidi-controls error BiDi controls (U+202A-U+202E, U+2066-U+2069) on any line, and directional marks (U+200E, U+200F) in code

Error rules do not apply to doc-blocks (JSDoc /** … */, Python docstrings, and /// doc-comment lines — dartdoc, rustdoc, C# XML doc): contract documentation for classes and functions may be any length. Inside doc-blocks, changelog markers (error) and signature restatement "This function…" (warning) are still caught. A long-comment line whose text carries a why-marker (because, since, otherwise, workaround, to avoid, by design, trade-off, e.g. — plus RU/DE/FR/ES equivalents) is not flagged: a long single-line why-comment is legitimate, a multi-line narrative is not. The same why-marker exempts a 2-line comment run (a wrapped why that would fit one line); 3+ lines are still flagged. Gherkin .feature specs exempt multi-line-comment and vend/file-summary-header entirely — scenario narration and header blocks there are a normal form — while the other slop rules (changelog-marker, step-numbered, markdown-in-comment) keep firing.

Text rules (step-numbered, markdown-in-comment, this-function-opener, cross-file-ref) are matched on the text after the comment marker is stripped, so they work in every profile — # Шаг 3 in yaml and -- Step 3 in sql are caught identically. step-numbered, this-function-opener and changelog-marker understand RU + EN + DE + FR + ES ("Шаг N", "Schritt N", "Étape N", "Diese Funktion", "au lieu de", "ya no", etc.); other natural languages are not covered. Structural rules (multi-line, divider, header, todo) do not depend on the wording language. step-numbered and markdown-in-comment do not fire inside doc-blocks. vend/obvious-comment is additionally gated by a per-file density filter: a file's obvious-comment findings are dropped unless they make up at least 2% of its non-empty, non-comment lines — hand-written files carry a few terse comments, while generated files narrate every few lines.

Full rationale per rule (Why / Instead of / Write / Ignore it when, from the same RULES table):

node skill/scripts/scan.mjs --explain <rule-id>

Rule ids and service labels are EN; messages and rationales are RU. The vend/ prefix = rules vendored from external pattern catalogs.

The detector sees inline comments after code (const x = 1 // was), block comments without a marker on middle lines, doc-blocks of any length (contract JSDoc/docstrings////), UTF-16 files with a BOM; zero-width characters (U+200B–U+200F, U+FEFF) are stripped when matching markers; vend/zero-width-chars flags U+200B, U+200C, U+2060, U+FEFF and a non-emoji U+200D on raw lines, and vend/bidi-controls flags the BiDi overrides and isolates (U+202A–U+202E, U+2066–U+2069) on raw lines including inside comments, while the directional marks U+200E/U+200F are flagged only in the code part of a line — they are legitimate RTL typography in comments and prose. Escape forms in the source are flagged too; a ZWJ inside emoji sequences and a BOM at position 0 are not flagged. CJK adjacency to Latin letters or digits is checked only in the code part of a line: Chinese comments and i18n strings without Latin adjacency are legitimate. In prose formats (.md/.mdx/.html/.xml/.rst/.adoc) CJK adjacency is not checked at all: mixed JP/CN prose with Latin brand names is normal there. Not scanned: languages without a profile (see the table above; .m is ambiguous), binary and office formats; --staged and --diff do not see untracked files. Warnings do not block the gate unless --strict is given. Non-ASCII file names are supported in diff modes.

What the full scan walks. Inside a git repository, scan enumerates files via git ls-files --cached --others --exclude-standard, so everything git-ignored is invisible: __pycache__, .venv, build outputs, vendored trees, downloaded toolchains, packaged runtimes, minified bundles, compiled .dart.js. Outside a repo it falls back to a directory walk. On top of that, hard-coded artifact directories are always skipped (venv, build, .next, target, out, .gradle, Pods, __pycache__, .idea, .codegraph, site-packages, .dart_tool, coverage, .git, node_modules, dist).

Generated files are exempt from the slop rules in every mode (full scan, diff, write-time gate) — detection layers, user signals and knobs are in Generated code. A file whose first 8 KB contain a NUL byte is treated as binary and skipped (UTF-16 with a BOM is decoded first, so it is not mistaken for binary). YAML block scalars (key: |, - >) are string content, not comments: their lines are never flagged.

License headers are exempt from the multi-line rule, and a shebang line never merges with the comment line that follows it. In .go files, a run of 2+ // comment lines directly above a func/type/const/var declaration (methods included) whose first word names the declared identifier is a Go doc comment — exempt from multi-line-comment and vend/file-summary-header like a doc-block; the other rules still apply. Inline comments are detected by the profile markers (//, #, --, %, ;, !) except py/fs floor division (//). A directory argument that lies outside the current repository is walked directly (the git listing only covers the repo itself).

Generated code

Generated files are exempt from the slop rules in every mode (full scan, diff, write-time gate). Three detection layers, any one is enough:

  1. Unique file-name suffixes nobody writes by hand — build_runner (*.g.dart, *.freezed.dart, *.gr.dart, *.chopper.dart, *.pb*.dart), protoc (*_pb2.py, *_pb2.pyi, *_pb.go, *_grpc.pb.go, *.pb.cc/h/hpp/cpp, *.pb.mojom.*), Kubernetes zz_generated.*, stringer *_string.go, sqlc (*.sql.go, *.querier.go), .NET (*.Designer.cs, *.g.i.cs, *AssemblyAttributes.cs, GlobalUsings.g.cs), bundles (*.min.js, *.min.css, *.bundle.js). Ambiguous names (*.gen.go, *.generated.ts, *_Factory.java, *.g.cs, mock_*.go, *.d.ts) are deliberately not name-exempt — too many false positives; they are exempt only through a header below.
  2. Tool-named headers in the first 10 lines: @generated, Code generated by … DO NOT EDIT, generated code - do not modify by hand, <auto-generated, automatically generated by rust-bindgen, @generated by prost-build, @javax.annotation.Generated(, generated by openapi-generator, code generated by sqlc.
  3. Ad-hoc generators: both generat…/codegen and do not edit/modify within the first 10 lines. A bare DO NOT EDIT without a generator word does not exempt — hand-written policy files stay linted.

Two user signals sit on top of the layers: a .gitattributes line with linguist-generated (the GitHub linguist convention) and the generatedPaths list in .stop-ai-slop.yaml (same prefix semantics as excludePaths).

Semantics: on a generated file the slop rules are exempt, but the security rules vend/zero-width-chars, vend/bidi-controls and vend/cjk-noise still fire — poisoned codegen is a supply-chain signal, not a style issue. The write-time gate follows the same rule: generated files are exempt from the slop rules, but the security rules still fire on them. Set scanGenerated: true in .stop-ai-slop.yaml to lint generated code like any other file.

Bench (FP regression cohort)

--bench pins a cohort of 8 mature open-source repositories (expressjs/express, pallets/flask, gin-gonic/gin, tokio-rs/tokio, rack/rack, redis/redis, PowerShell/PowerShell, vuejs/vue — each pinned in the BENCH_COHORT table in skill/scripts/scan.mjs to a commit dated before 2025-01-01), scans every one with the raw rule pipeline (no config, no baseline), and counts findings per rule id. Comparing the counts against the committed bench-history.json catches false-positive regressions: a rule whose count grew on a pinned tree means a rule change introduced new false positives. Counts only — no scoring.

node skill/scripts/scan.mjs --bench         # per-repo per-rule counts table + delta vs bench-history.json
node skill/scripts/scan.mjs --bench-write   # overwrite bench-history.json with the current counts (exit 0)

Semantics: any increase of a rule's count on any repo is a regression — --bench prints the delta entries and exits 1; decreases and equal counts are fine (exit 0). A missing bench-history.json is treated as empty history (every finding counts as an increase), with a hint to run --bench-write. The history file is committed to this repository — it is the pinned FP baseline.

The first run needs network and git: each repo is fetched once (git init + git fetch --depth 1 <sha>) into ~/.cache/stop-ai-slop/bench/<owner>--<name> (override with the STOP_AI_SLOP_BENCH_CACHE environment variable). Later runs reuse the cache; a cached checkout at the wrong SHA is re-fetched. A fetch failure exits 2.

Refreshing the cohort: edit BENCH_COHORT (repo + pinned SHA), re-run --bench-write, and review the resulting bench-history.json delta in the PR — every count increase must be explainable as a true positive, otherwise the rule change is a false-positive regression.

DetBench (recall regression corpus)

--detbench runs the detector over detbench-corpus.json — a committed corpus of real agent comment slop mined from the write-time gate's audit log (the findings of every blocked event). The bench cohort pins false positives on human code; detbench pins recall on real agent output: a rule change that stops firing on a corpus entry fails the mode with exit 1. Each entry stores the source file name (for profile resolution), the reconstructed line run, and the rule that must fire.

node skill/scripts/scan.mjs --detbench

Corpus semantics: a multi-line-comment finding is recorded as its line repeated three times — runs of 3+ lines always flag, so the reconstruction is immune to the two-line wrapped-why exemption; single-line rules are recorded as-is. Only error rules appear: the write-time gate blocks error findings only, so warnings never reach the audit log. long-comment findings cannot be mined — the audit log truncates the recorded text at 120 characters, and a truncated line can never exceed the length limit again.

Regeneration: node skill/scripts/gen-detbench.mjs --write re-mines the local audit log (STOP_AI_SLOP_LOG overrides the path), keeps only entries that fire today, and reports the dropped ones; --check (CI-safe — it never reads the log) fails when a committed entry stops firing, the recall-regression tripwire, or when shape or sort order drifts; --normalize re-sorts the file after manual edits. The corpus path resolves from the git root (STOP_AI_SLOP_DETBENCH overrides). Refresh discipline mirrors --bench-write: regenerate, review the diff — corpus texts derive from real blocked edits, so rewrite identifying content before committing — and commit.

The current corpus was mined 2026-10-15 from one machine's audit log: 180 blocked write-time edits → 181 unique entries after deduplication, all multi-line-comment.

Playground

A zero-backend page that runs the real detector (skill/scripts/src/detect.mjs) in the browser: Vite bundles the actual modules, nothing is vendored. The built page is committed at playground/dist/index.html and works offline, including opened straight from disk (file://).

cd playground && npm install && npm run build

Then open playground/dist/index.html. The playground is a devDependency-isolated demo: it is not part of the shipped npm package (the files list does not include playground/). GitHub Pages deployment is a future step.

Comparison with alternatives

Facts from the competitors' READMEs, GitHub metadata and npm download counts, checked 2026-09-29. Traction = GitHub stars and npm downloads per month on the check date.

Dedicated slop linters

Tool What it scans Languages / natural languages Blocks at write Gate model Custom rules Runtime Traction
stop-ai-slop comments in code, 16 rules 160 extensions, 26 file names, 34 profiles / RU, EN, DE, FR, ES; messages RU or EN (--lang) yes — OpenCode plugin, Claude Code, Codex, Gemini, Qwen, Devin hooks policy: rule → exit 1; errors vs warnings RULES table in one file; severity remap; --explain Node >= 18, zero deps, one .mjs scanner 449 dl/mo
windbag change-narration comments, 6 rules (HISTORY_NARRATION, HEDGE_LANGUAGE, TICKET_ID, CROSS_FILE_REF, VERBOSE_COMMENT, OBVIOUS_COMMENT) Python, JS/TS, Terraform, Rust, Go, Java, SQL (dbt/SQLMesh), YAML/HTML/MD / EN Claude Code PostToolUse hook; pre-commit; no OpenCode error rules fail the check, warn rules report none documented Rust binary shipped as a PyPI wheel 25★
aislop code slop: 50+ rules — narrative comments, swallowed exceptions, as any, dead code, hallucinated imports 10 targets (TS, JS, Expo/RN, Python, Go, Rust, Ruby, PHP, C#, C/C++) / EN hooks for 10 agents (Claude, Cursor, Gemini, Pi, Codex, Windsurf, Cline, Kilocode, Antigravity, Copilot); no OpenCode score 0–100, failBelow; CI mode; SARIF; MCP server per-rule severity; new rules only in their repo npm + optional engines (biome, ruff, oxlint); PyPI; Homebrew 655★, 47k dl/mo
slop-scan AI-associated code patterns; hotspots; repo comparison JS/TS / EN no score + normalized metrics (per KLOC, per function) config and plugins npm 319★, 35k dl/mo
anti-slop low-evidence code patterns, an Oxlint ruleset meant to be vendored TS/JS / EN no rule → error (Oxlint) vendored by design — edit your copy Oxlint plugin; no npm package 5.0k★
AI-SLOP-Detector "fake-done" code: 27 checks — empty stubs, unresolvable imports, dead pipelines, buzzword-padded docs Python first; JS/TS and Go extras / EN no 0–100 risk score per file; soft/hard/quarantine CI gates; MCP domain presets; local calibration Python (PyPI), offline, deterministic; VS Code extension 96★
gptlint best-practice violations judged by an LLM; rules authored in markdown JS/TS (MVP) / any language the LLM reads no LLM verdict; eslint-style CLI and config custom rules are first-class (markdown) npm + LLM API keys or a local model; caching 297★, 138 dl/mo
dotnet-slopwatch LLM reward hacking: disabled tests, suppressed warnings, swallowed exceptions, masking delays .NET / EN Claude Code hook; CI rule → block — .NET tool (NuGet) 110★
grain AI code patterns as an agentic repair queue (JSON violations, multi-session worklog) Python / EN no rule → error; fixable flag per violation — Python 34★, idle since 2026-04
sloppylint over-engineering, hallucinations, dead code Python / EN no findings report — Python 90★, idle since 2025-12
ai-slop-linter prose: commit messages, PR descriptions, docs; 21 tells any text / EN tells; their own benchmark: the em dash hits correct Russian prose 24 times per 1000 words no — commit-msg hook and commitlint rule gate the message at commit time weighted score per 1000 words; SARIF ignore/only per file npm, zero runtime deps 1.6k dl/mo
vibecheck-slop-stopper 78 grep rules across all slop categories 9 stacks / EN no — GitHub Action, CLI; the Claude Code skill asks the LLM to run the patterns via its Grep tool severity levels rules.toml Python + ripgrep (the skill itself: no deps) 0★, idle since 2026-04

Smaller and newer tools

Checked the same date, one line each: dannote/sloplint (AST-based, multilingual; idle since 2026-02), bibekmhj/sloplint (the first JVM AI-slop linter), rbaumier/comply, thrash-d/slop-linter (Vale rules + a Claude Code hook for prose and code comments), almcc/slop-linter (a Jev LLM classifier), Aaryan-9/ai-slop-remover (comment noise among its detectors), agiwhitelist/auteur (1035★ — a website-directing skill whose ship-gate embeds an anti-slop linter; design domain), mattpocock/slopwatch (51★, undocumented), LanNguyenSi/agent-dx (a monorepo toolkit whose slop-detector lints PRs).

Adjacent approaches

SaaS review bots: CodeRabbit ships a named "Slop Detection" — PR-level AI-spam triage: early-access, non-blocking, exempts your own members, from $24/dev/mo. Greptile, Graphite Diamond and Qodo ship no slop feature — a comment policy can only ride their custom LLM rules. Codacy and SonarQube wrap community linters: commented-out code (S125) and TODO tags (S1135), no narrative detection. Classic linters (ESLint no-warning-comments and friends, Biome, Checkstyle, ktlint, detekt, godot/revive) enforce comment style — position, case, punctuation, TODO vocabulary — and none detect changelog narration or step numbering; the closest are eslint-plugin-write-good-comments (prose quality inside comments) and Vale (tree-sitter comment extraction in 28 languages with style packages — no structural slop rules). Community agent skills (dashed/claude-marketplace comment-slop, kubosho/anti-slop-comment, manutej/craft) encode the same taxonomy as advisory LLM instructions — no deterministic gate.

Where stop-ai-slop is worse

  • Only comments. Code-level slop — swallowed exceptions, as any, dead code, hallucinated imports, reward-hacked tests — is out of scope: aislop (50+ rules, 10 language targets), dmmulroy/anti-slop, AI-SLOP-Detector, grain and dotnet-slopwatch cover it.
  • No prose scanning. Commit messages, PR descriptions and docs are ai-slop-linter's territory (its commitlint rule and commit-msg hook gate those at commit time).
  • Line-based extraction, not grammars. windbag reads comments through real grammars: a # inside a quoted YAML scalar stays data, .sql is parsed as Jinja templates (dbt/SQLMesh), fenced Markdown blocks are skipped. Our inline detection tracks template literals with ${} interpolation in the JS/TS family profiles, but stays line-local: a line that starts inside an open multi-line template literal cannot be tracked without file state — a documented limitation.
  • Comment length is an absolute limit. Our long-comment is 120 characters; windbag's VERBOSE_COMMENT measures the comment against the code it documents. (After the 2026-09-29 comparison we adopted two of windbag's rules as vend/cross-file-ref and vend/obvious-comment; the relative-length rule remains theirs.)
  • No semantic matching. gptlint (LLM) and almcc/slop-linter (Jev model) judge meaning and catch paraphrased slop; our markers are dictionaries in RU+EN+DE+FR+ES — no ZH/JA phrase detection.
  • No IDE extension. AI-SLOP-Detector ships a VS Code extension with inline findings and a status-bar score; our IDE story is a tasks.json problem matcher plus an IDEA File Watcher template.
  • Adoption and distribution. aislop: 655 stars, 47k downloads/month, npm + PyPI + Homebrew, score badges, aislop agent repair sessions driving Codex/Claude/OpenCode worktrees. slop-scan: 35k downloads/month. stop-ai-slop: 449 downloads/month, npm + a GitHub Packages mirror + a Homebrew formula (this repository as its own tap).

What only stop-ai-slop has

  • the OpenCode write-time gate — no competitor blocks a write inside OpenCode: aislop's hook list covers ten agents without OpenCode, windbag blocks only in Claude Code;
  • a zero-dependency single-file scanner — aislop drives external linter engines, windbag ships a Rust binary, vibecheck needs Python + ripgrep;
  • Russian, German, French and Spanish natural-language markers;
  • invisible-character rules with escape-form awareness: zero-width and BiDi controls, CJK glued to Latin in code — a poisoned-codegen supply-chain signal;
  • a fingerprinted baseline (rule id + comment text) that survives line shifts and still flags re-pasted legacy slop.

stop-ai-slop is deliberately narrower: comment policy only. Swallowed exceptions, as any, dead code — the territory of aislop, dmmulroy/anti-slop, grain and dotnet-slopwatch; EN prose and commit messages — ai-slop-linter. It complements them exactly where they do not reach: the moment of the edit in OpenCode, and the Russian changelog markers.

License

MIT — see LICENSE.

Yorumlar (0)

Sonuc bulunamadi