Skip to main content

Slop Guard

Anti-pattern detection and gate for agent-written code.

Slop Guard runs as a plugin for Claude Code, Codex CLI and Grok, wrapping every edit in three detection layers: a fast synchronous gate on every write, a background medium-tier check after each edit batch, and a slow gate at turn end before the agent declares completion. It also ships as a standalone PyPI package so you can call slopguard directly from scripts and CI.

Status: the PyPI package is not yet published. Install from source (see As a PyPI package) or use the plugin directly. Once published: pip install aisdlc-guard (command: slopguard).


Contents


Installation

As a Claude Code / Codex / Grok plugin (hooks-based)

Slop Guard is a hooks-based plugin, not an MCP server. claude mcp add does not apply.

Local checkout:

git clone https://github.com/emgiezet/slopguard.git ~/.local/share/slopguard
claude --plugin-dir ~/.local/share/slopguard
# Or for omp:
omp --plugin-dir ~/.local/share/slopguard

The checkout contains .claude-plugin/plugin.json and hooks/hooks.json. Plugin hosts set CLAUDE_PLUGIN_ROOT to this directory; the PyPI console script alone does not install or activate agent hooks.

For the omp harness: reference the checkout path as "source" in your .omp-plugin/marketplace.json plugin entry (see the existing entry in this repo for the exact shape).

omp adapter caveat: extensions/omp.mjs currently wires only PreToolUse and SessionStart events. The fast/medium PostToolUse detection tiers and the Stop gate are not forwarded under omp.

For Codex CLI or Grok: see plugin.json and your host's plugin documentation for the installation method.

As a PyPI package

Requires: Python ≥ 3.9, bash, jq; on macOS, install GNU coreutils (brew install coreutils) and put $(brew --prefix coreutils)/libexec/gnubin on PATH for the Bash hooks (timeout, touch -d, and related commands).

Until the first PyPI release, install the CLI from the standalone repository (pin a commit SHA in automated environments):

uv tool install git+https://github.com/emgiezet/slopguard.git
slopguard version

Once published, pip install aisdlc-guard or uv tool install aisdlc-guard works. For local tool setup, use slopguard doctor and slopguard doctor --install.

The wheel bundles the complete plugin tree (hooks, rules, skills, configs, tools lockfile) under slopguard/_plugin/. The slopguard entry point discovers this bundled root automatically — no SLOPGUARD_PLUGIN_DIR or CLAUDE_PLUGIN_ROOT override is required.

Isolated root: the installed entry point always sets CLAUDE_PLUGIN_ROOT to the bundled _plugin/ directory, overwriting any inherited value. This ensures the CLI sources its own assets even when run inside an environment where a plugin host has already exported CLAUDE_PLUGIN_ROOT. Plugin hosts invoke bin/slopguard directly from the checkout; they never call the PyPI entry point.

Development / source checkout — use bin/slopguard directly; it derives its own PLUGIN_ROOT from $SCRIPT_DIR at startup. Running pip install -e . gives you the slopguard command on your PATH; it falls back to the project root automatically when the wheel-bundled _plugin/ directory is absent.


Configuration

Three files let you tune Slop Guard without forking it.

.slopguard.json — declare which stacks the guard covers and, in a monorepo, which paths belong to which stack. If absent, stacks are detected automatically. See docs/recommended-project-settings.json for a full example snippet.

.slopguard/mapping/<tool>.yaml — retune or extend rules for tools the guard already runs. Format is identical to rules/mapping/<tool>.yaml; a project entry patches the plugin entry field by field. Lowering a severity is allowed (silencing a noisy rule is a legitimate project decision) and every downgrade is counted and reported.

# .slopguard/mapping/sqlfluff.yaml
rules:
  AM04:
    ap_id: AP-SQL-MAINT-002
    severity: warn
    category: maintainability
    cwe: ""
  S608:
    severity: error

.slopguard/tools/<name>.yaml — bring in a linter the guard does not pin. The guard only runs a binary your project already installs or whose sha256 you declared — it never installs a descriptor's tool itself.

# .slopguard/tools/sqlfluff.yaml
name: sqlfluff
tier: fast
match:
  globs: ["**/*.sql"]
  stacks: [sql]
resolve:
  project: [".venv/bin/sqlfluff", "vendor/bin/sqlfluff"]
run:
  args: ["lint", "--format", "json", "{file}"]
  timeout: 8
parse:
  format: json
  jq: '.[] | .violations[]? | {rule: .code, line: .line_no, message: .description}'

Every file under .slopguard/ requires human approval to change (the pre-write hook asks, same as .slopguard.json).

Adopt a baseline config to opt in to full reporting for a tool (security overlay only reports security findings without a project config):

slopguard adopt-config ruff        # copies configs/baseline/ruff.toml
slopguard adopt-config mypy        # writes .slopguard/tools/mypy.yaml
slopguard adopt-config pylint      # writes .slopguard/tools/pylint.yaml
slopguard adopt-config             # list what is not yet adopted

Plugin options (set via the Claude Code plugin options dialog or in .slopguard.json):

Option Values Default
enforcement_mode advisory | balanced | strict balanced
stop_gate true | false true
sast_engine opengrep | semgrep | none opengrep
allow_network true | false false
tool_source project-first | plugin-only | project-only project-first
config_source overlay | full | project-only overlay
require_docs_lookup true | false true
dependency_freshness off | warn | error warn
dependency_cooldown_days integer (string) "3"

What runs and when

Slop Guard wraps every edit in three layers, each at a different cost point.

Fast tier — on every write, foreground (p95 < 2 s)

Synchronous PostToolUse hook. Runs immediately after each file write or edit and returns findings to the agent before it continues.

Tools: Ruff (Python), ESLint without type-info (JS/TS), hadolint (Dockerfile), kube-linter (Kubernetes YAML), zizmor (GitHub Actions).

Findings are filtered to the changed lines only — existing problems in unchanged code are not re-reported every turn. A missing tool is skipped silently with a one-time session note; it never blocks the write.

Medium tier — after the edit batch, background (< 60 s)

Async PostToolUse hook with asyncRewake. Starts after the fast hook; the agent continues working while it runs. Only wakes the agent back if it finds new problems at error severity or above.

Tools: PHPStan (PHP), golangci-lint with --new-from-rev (Go), ESLint with type-info (JS/TS), tflint (Terraform), Checkov per-file (IaC), Opengrep with the plugin's own rules (all stacks), jscpd directory-scoped (duplication).

A three-second debounce collapses a rapid edit batch into one run.

Slow tier — at turn end, Stop gate (< 5 min)

Stop hook. Runs before the agent declares the turn complete. Covers analysis too expensive for per-file runs: Psalm taint-analysis (PHP), Checkov on the whole changed directory (IaC), Betterleaks on the full session diff.

Also re-runs slopguard deps-check when any dependency manifest changed, and reports (never blocks) if framework files were edited without a Context7 documentation lookup in the session.


Stop gate and enforcement modes

The Stop gate holds the agent on the current turn until every blocker in changed code is resolved.

Mode Blockers Errors Warnings
advisory additionalContext only additionalContext only additionalContext
balanced (default) blocks turn end additionalContext in Stop additionalContext
strict blocks turn end blocks turn end additionalContext

Loop protection: the same blocker in the same file can hold the agent at most twice in a row. After that it is downgraded to a warning and a message is shown to the user — so a genuinely unresolvable issue never spins the agent indefinitely.

To disable the Stop gate for a project, set stop_gate: false in .slopguard.json or in the plugin options.


Prevention skills

Alongside runtime checks, Slop Guard loads language-specific prevention skills that put the rules in the agent's context before it writes any code.

Skill Loads when editing
php-antipatterns *.php, *.blade.php
go-antipatterns *.go
python-antipatterns *.py
ts-react-antipatterns *.ts, *.tsx
node-antipatterns *.js, *.mjs, *.cjs
sql-antipatterns *.sql
iac-antipatterns *.tf, *.yaml, *.yml, Dockerfile (IaC context)
auth-antipatterns auth, login, session, token, password and OAuth paths
agent-discipline always — AP-AGENT-001 through AP-AGENT-011

auth-antipatterns is the one catalogue that is not a language: token entropy, reset-token storage and TTL, password hashing (argon2id, bcrypt cost, scrypt), session rotation and invalidation, JWT verification, re-authentication before a credential change, rate limits, account enumeration, OAuth2 state and PKCE, OTP attempt limits.

Always-on rules: AP-AGENT-002 asks only when a required test gate is weakened (a coverage threshold that drops or disappears, a test step removed from CI, or an edit to Slop Guard's own configuration). AP-AGENT-004 always blocks curl | sh and global installs, but a routine dependency add proceeds with a note and escalates to ask only when the package name resembles a popular one.

Session banners shrink once read. The always-on digest prints in full the first time a repository starts a session and becomes a one-line pointer afterwards (SLOPGUARD_DIGEST=full forces the long form back).

Tier-2 stacks (JVM, C#, Ruby, Rust) load their own skills with Opengrep-backed detection. SessionStart says which tier each detected stack runs at, so silence from the detector is never mistaken for a clean codebase.

Agent commands

/slop-guard:secure-review runs a read-only security-reviewer subagent over the current session's changed files. It cannot write or edit — it only reports.

/slop-guard:deslop <path> runs an editing subagent over a file or directory, removing duplicate blocks and dead code. Without --tests <cmd> it performs pure deletions only. --dry-run lists what would be changed without writing anything.


CLI reference

slopguard doctor             report tool status for the project's stacks
slopguard doctor --install   install or update the tools those stacks need
slopguard doctor --all       widen either command to every pinned tool
slopguard adopt-config       list tools without a project config
slopguard adopt-config <t>   copy the baseline config for <t> into the project
slopguard note-docs          record a Context7 lookup (reads hook JSON on stdin)
slopguard deps-check         check dependency freshness against registries
slopguard deps-check --json  emit a JSON array of verdicts (CI-friendly)
slopguard version            print version
slopguard help               full usage

deps-check environment knobs:

Variable Purpose
CLAUDE_PLUGIN_OPTION_ALLOW_NETWORK Must be true to enable registry queries
CLAUDE_PLUGIN_OPTION_DEPENDENCY_FRESHNESS off | warn | error
CLAUDE_PLUGIN_OPTION_DEPENDENCY_COOLDOWN_DAYS Freshness cooldown in days
SLOPGUARD_FETCH_CMD Override HTTP fetch for offline testing
SLOPGUARD_REGISTRIES_JSON Override registries file path

Tool resolution order (when tool_source=project-first):

  1. Project binary — vendor/bin, node_modules/.bin, .venv/bin, go tool -n
  2. Plugin binary — ${CLAUDE_PLUGIN_DATA}/tools/<name>/current/<name>, accepted only when the version matches the lockfile
  3. PATH binary — accepted only when the version matches the lockfile

Contributing

See docs/slop-guard-spec.md for the full specification. See .claude/playbooks/add-rule.md for the rule-addition sequence. See .claude/rules/catalog.md for catalogue conventions and verification.

Adding a rule in brief

  1. Add one entry to rules/catalog.yaml (AP-* id, fields per §4.9 of the spec).
  2. If the rule has detection: add a mapping entry in rules/mapping/<tool>.yaml and fixture files in tests/fixtures/<lang>/{bad,good}/.
  3. Regenerate skills: scripts/gen-skills.
  4. Add a changelog entry under ## Unreleased.
  5. Verify: bash tests/catalog_test.sh && CI= bash tests/run-tests.

Running the test suite

bash tests/run-tests            # unit + bash hook tests (needs jq)
bash tests/tool-integration     # integration tests (needs installed tools)
pytest tests/python/ -v         # installed-artifact Python tests (builds wheel)

The Python tests build a wheel from source and install it in a temporary virtual environment — no prior installation needed, but build and hatchling must be available (they are installed automatically if missing).

Use the SLOPGUARD_TEST_BIN env var to skip the build step and point at an already-installed binary:

pip install dist/aisdlc_guard-*.whl
SLOPGUARD_TEST_BIN="$(which slopguard)" pytest tests/python/ -v

Release process

Slop Guard has four version fields that must be kept in sync:

File Field
pyproject.toml [project] version
.claude-plugin/plugin.json "version"
plugin.json "version"
package.json "version"

To release a new version:

  1. Bump the version string in all four files in a single commit.
  2. Update CHANGELOG.md: move ## Unreleased items under a new ## <version> — <date> heading.
  3. Merge the commit to main.
  4. Push a tag: git tag v<version> && git push origin v<version>.

The publish.yml GitHub Actions workflow triggers on a release tag whose commit is on main, runs the tests, verifies the tag matches pyproject.toml, builds the wheel and sdist, and publishes to PyPI via Trusted Publishing (OIDC — no stored API token). The repository's owner-only v* tag ruleset and required emgiezet review on the pypi-publish environment protect this workflow; do not remove either control.

First-time PyPI setup (owner action):

  1. In your PyPI account's Publishing settings, create a pending publisher for the not-yet-published aisdlc-guard distribution (the command stays slopguard):
    • PyPI project: aisdlc-guard
    • GitHub owner: emgiezet
    • Repository: slopguard
    • Workflow: publish.yml
    • Environment: pypi-publish
  2. Only after the publisher exists, push v0.6.1 pointing at main. Approve the GitHub environment deployment when prompted. PyPI creates the project on the first successful release.

License

MIT — see LICENSE.

Third-party tool attributions are in THIRD_PARTY_NOTICES.md.

Metadata

Release files for aisdlc-guard 0.6.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aisdlc-guard 0.6.1
File Size Uploaded Provenance Actions
aisdlc_guard-0.6.1.tar.gz 596.3 kB With provenance Details for aisdlc_guard-0.6.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for aisdlc-guard 0.6.1
File Interpreter ABI Platform Actions
aisdlc_guard-0.6.1-py3-none-any.whl Python 3 none any Details for aisdlc_guard-0.6.1-py3-none-any.whl

Total release size: 1.1 MB

Release files / aisdlc_guard-0.6.1.tar.gz

Download URL aisdlc_guard-0.6.1.tar.gz
Size 596.3 kB
Tags Source
SHA-256 checksum
How to use checksums
57dee839b488e5220c9339575c30f3c2ea52aae26e07bce05cd766438fd8c030
BLAKE2b-256 checksum
How to use checksums
4e156ff2b0a44c7a17c5b1a0fc4aad5bc110f13fe821b4292a8d529d92d7df63
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

Release files / aisdlc_guard-0.6.1-py3-none-any.whl

Download URL aisdlc_guard-0.6.1-py3-none-any.whl
Size 516.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
45d411b913931fa0869d3bf160e02b863502b8e68b8c2a3f622420f0e9ba8d3e
BLAKE2b-256 checksum
How to use checksums
4d993bc89e7f6141428a7f1e0217fc3d5f7e95e41a4715bfcce9f8ee6f31ca90
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

Security for aisdlc-guard 0.6.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for aisdlc_guard-0.6.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.6.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page