# Desloppify: an agent harness that turns scan results into a fix queue

> Desloppify is a Python CLI that scans a codebase for mechanical and subjective quality issues, scores them by dimension, and hands an AI coding agent an ordered queue to work through. It is opinionated about state, scoring, and what counts as done.

**peteromallet/desloppify** — Agent harness to make your slop code well-engineered and beautiful.

- Repository: https://github.com/peteromallet/desloppify
- Website: https://desloppify.it/
- Stars: 3,158 · Forks: 254
- Language: Python
- License: NOASSERTION
- Published: 2026-09-24 · Updated: 2026-09-24 · Language: en
- Canonical page: https://hysenlabs.com/projects/peteromallet-desloppify

## What Desloppify solves, and who it is actually for

Most linters stop at a list. You get 400 findings, you sort them yourself, and the list is stale the moment you start fixing. Desloppify's premise is different: it treats codebase quality as a persistent, scored project with an execution queue that survives across sessions. The README describes it as giving your AI coding agent "the tools to identify, understand, and systematically improve codebase quality", combining mechanical detection with LLM review and then working through a prioritized fix loop.

The target user is narrower than the tagline suggests. This is built for someone who already has an agent (Claude, Cursor, Codex, Copilot, Droid, Windsurf, Gemini or Rovodev are the skill targets named in the README) and wants that agent to work from a structured backlog instead of improvising. If you are not running an agent, the tool still scans and scores, but the loop that makes it distinctive goes unused.

There is a second audience worth naming. Desloppify's scoring is designed to resist gaming, and the README claims a score above 98 "should correlate with a codebase a seasoned engineer would call beautiful". That is a strong claim, and it is the kind of claim you should test against your own codebase before trusting it as a gate.

## The scan, review, triage, execute, rescan pipeline

The README lays out a five-stage cycle: scan, score, review, triage, execute, rescan. Each stage has a distinct job, and the separation matters.

Scan runs mechanical detectors across the codebase: dead code, duplication, complexity, test coverage gaps and naming issues. Each issue is scored by dimension, with File health, Code quality and Test health given as examples. Review is where an LLM assesses dimensions that detectors cannot reach: naming, abstractions, error handling patterns, module boundaries. Those subjective scores sit alongside the mechanical ones in the same score.

Triage is the part most tools skip. The agent observes findings, reflects on patterns, organizes issues into clusters, and enriches them with implementation detail. The output is an ordered execution queue. Critically, the README states that only items explicitly queued appear in `next`; before triage, all mechanical issues are visible in the queue sorted by impact, which the README itself calls noisy.

Execute is the loop itself: `next` to see the current item, fix it, `resolve` it, run `next` again. Rescan verifies improvements and catches cascading effects. State lives in `.desloppify/`, which is why progress carries across sessions and why the README tells you to add that directory to `.gitignore`.

The gaming resistance is structural rather than a filter. Wontfix items widen the gap between lenient and strict scores, and re-reviewing a dimension can lower the score if the reviewer finds new issues. That second property is unusual: most quality scores only go up when you re-run them.

## Installing Desloppify and running a first scan

The package requires Python 3.11 or newer. The README's recommended install pulls the full optional dependency set, which includes tree-sitter, bandit, Pillow, PyYAML and defusedxml. Run it with pip:

```bash
pip install --upgrade "desloppify[full]"
```

Next, install the workflow guide for your agent. The README lists claude, cursor, codex, copilot, droid, windsurf, gemini and rovodev as the available targets:

```bash
desloppify update-skill claude
```

Before scanning, the README advises checking for directories that should be excluded (vendor, build output, generated code, worktrees) and excluding the obvious ones. The `exclude` subcommand takes a path:

```bash
desloppify exclude vendor/ build/
desloppify scan --path .
```

`--path` is the directory to scan. Use `.` for the whole project or a subdirectory such as `src/`. After the scan completes, the execution queue is read with `next`:

```bash
desloppify next
```

The README describes `next` as the execution queue from the living plan, not the whole backlog. It reports what to fix now, which file, and the resolve command to run when the fix is done. `desloppify backlog` is the command for inspecting broader open work that is not currently driving execution.

## Monorepos need one scan per project, not one scan of the root

This is the constraint most likely to bite a new user, and the README is explicit about it. Scanning a parent directory that contains multiple programs mixes state and path context across unrelated codebases, producing unreliable results. Each `--path` target should be a single coherent project.

For a frontend and backend in sibling directories, the README's example scans them separately and pins the language for each:

```bash
desloppify --lang typescript scan --path ./frontend
desloppify --lang python scan --path ./backend
```

Because state is maintained per language, a TypeScript frontend and a Python backend can coexist in the same workspace without conflict, provided you target them individually. In CI, the README repeats the advice: for monorepos, run one job or matrix entry per project path rather than scanning the workspace root.

There is a related limitation worth stating plainly. True incremental or diff-only scanning is not the supported model yet, according to the README. Desloppify in CI works best as a full-codebase health gate. If your workflow depends on scanning only changed lines in a pull request, this is the wrong tool for that job, and the README says so rather than leaving you to discover it.

## Using Desloppify as a CI gate, and the Java runner caveat

The CI profile is a different shape from the interactive loop. `--profile ci` skips slow and subjective phases and bypasses the mid-cycle scan queue gate so a job can collect a fresh mechanical snapshot. `--no-badge` suppresses badge generation. The README's minimal GitHub Actions example runs both commands after installing the full extras:

```yaml
name: desloppify

on:
  pull_request:
  push:
    branches: [main]

jobs:
  health:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - run: pip install --upgrade "desloppify[full]"
      - run: desloppify scan --path . --profile ci --no-badge
      - run: desloppify status --json
```

`status --json` is the machine-readable output a script can use to read the strict and objective scores and enforce a threshold of your own choosing. Desloppify does not ship a threshold policy; that decision stays with you.

One environment variable is documented for constrained runners. On Java CI machines, the PMD detector defaults to `--threads 0` to avoid worker-thread fanout. Setting `DESLOPPIFY_PMD_THREADS` to a value such as `2` or `0.5C` increases throughput. Note that `0.5C` is a fractional-core value, not a plain integer, so a script that validates the variable as an int will reject a legitimate setting.

## Language depth varies more than the headline number suggests

The README says Desloppify currently supports 29 languages, but the support is tiered, and the tiers are not equivalent. Full plugin depth covers TypeScript, Python, C#, C++, Dart, GDScript, Go and Rust. Ruby, Java, Kotlin and 18 others get generic linter plus tree-sitter support.

That distinction should drive your evaluation. A generic linter and tree-sitter path will surface syntax-level and lint-level findings, but the deeper detectors that feed the mechanical dimensions are the ones built per language. If your stack is Ruby or Kotlin, expect a thinner scan than a Go or Rust project would get, and expect the subjective review dimensions to carry more of the score.

C++ has its own wrinkle. The README states that `compile_commands.json` is the primary analysis path, and that Makefile repositories fall back to best-effort local include scanning. Best-effort include scanning is a materially weaker signal about dependency structure than a compilation database, so a CMake project with an exported `compile_commands.json` will get more reliable results than a hand-written Makefile project.

The optional dependency groups reflect this tiering too. `treesitter`, `csharp-xml`, `python-security`, `scorecard` and `plan-yaml` are separate extras, and `full` bundles them. Installing the base package without extras leaves those detectors unavailable, which is worth knowing if your CI installs the bare package name.

## How Desloppify differs from running a linter plus a review prompt

The honest alternative is what most teams already do: run ESLint, Ruff, golangci-lint or an equivalent, pipe the output into a prompt, and ask the agent to fix things. That approach has real advantages. It is stateless, it needs no new dependency, and the linter is a tool your team already understands.

The difference is what happens between runs. A linter report is regenerated from scratch every time; there is no memory of what you decided to leave alone, no queue ordering, and no way to distinguish a new regression from a finding you triaged last week. Desloppify persists state in `.desloppify/` and separates the full backlog from the execution queue, which is the mechanism that lets an agent work across multiple sessions without re-litigating settled decisions.

The scoring model is the second difference. A linter gives you counts. Desloppify gives you dimension scores plus a strict score, and the README claims the scoring resists gaming through wontfix widening and re-review that can lower scores. Whether that holds for your codebase is an empirical question. The README's own framing, that a score above 98 should correlate with a codebase a seasoned engineer would call beautiful, is a hypothesis you can falsify by scanning a project you know well and reading the result critically.

There is also a cost the alternative does not have. Desloppify introduces a state directory, a per-language state model, a triage step that gates what appears in `next`, and a CI profile that deliberately skips subjective phases. That is more moving parts than a linter invocation, and the README's own warning about pre-triage queue noise suggests the early experience is not tidy.

## Maintenance, licence and what to check before adopting

The repository is not archived. The last push was on 2026-05-13, which is more than four months before today, so treat the project as one with a recent release but no evidence of continuous activity in the months since. The v1.0 release is dated 2026-05-13, the same day as the last push, following v0.9.15 on 2026-04-06 and v0.9.14 on 2026-03-24. The cadence in the months before v1.0 was roughly every two to three weeks, and then it stops at the 1.0 tag.

Upgrade cost is low on the surface. The package is pure Python with no required runtime dependencies (`dependencies = []` in pyproject.toml), so the base install is small and the optional extras are opt-in. The `full` extra pins `tree-sitter-language-pack>=0.3,<1.8`, an upper bound that will eventually need attention if the language pack moves past 1.8. The Makefile shows the project's own CI targets, including `ci`, `ci-fast`, `lint`, `typecheck`, `arch`, `ci-contracts`, `integration-roslyn`, `tests`, `tests-full`, `sync-docs` and `package-smoke`, which tells you the maintainer runs a multi-gate pipeline rather than a single test command.

The licence needs a direct look. The repository's licence field reports NOASSERTION, and pyproject.toml declares `license = {text = "OSNL-0.2"}`. OSNL-0.2 is not a licence identifier this article can interpret for you, and the classifier metadata says Production/Stable, which is a claim about maturity rather than a legal statement. Read the LICENSE file at the repository root and route it through whatever process your organisation uses for non-standard licences. Do not assume it behaves like MIT or Apache-2.0 because the package is on PyPI.

## Conclusion

Adopt Desloppify if you already run an AI coding agent against a single coherent project directory and want a persistent, scored work queue rather than a one-shot lint report. Skip it if you need diff-only incremental scanning, since the README states that true incremental or diff-only scanning is not the supported model yet. Before committing, verify three things: that your language has full plugin depth rather than generic tree-sitter support, that .desloppify/ is in your .gitignore, and that the OSNL-0.2 licence text in the LICENSE file matches what your organisation accepts. For C++ projects, confirm whether compile_commands.json exists, because the README treats it as the primary analysis path and falls back to best-effort local include scanning for Makefile repositories.

## FAQ

### What Python version does Desloppify require?

Desloppify requires Python 3.11 or newer. The README states this in the install instructions, and pyproject.toml sets requires-python to >=3.11 with classifiers for 3.11, 3.12 and 3.13.

### How do I install the Desloppify skill for my coding agent?

Run desloppify update-skill followed by your agent name. The README lists claude, cursor, codex, copilot, droid, windsurf, gemini and rovodev as the available targets.

### Can I scan a monorepo with Desloppify in one pass?

No. The README states that scanning a parent directory containing multiple programs mixes state and path context across unrelated codebases and produces unreliable results. Each --path target should be a single coherent project, scanned separately.

### Does Desloppify support diff-only scanning in CI?

Not according to the README, which states that true incremental or diff-only scanning is not the supported model yet. It recommends using the ci profile as a full-codebase health gate and comparing full results across runs.

### What licence does Desloppify use?

pyproject.toml declares license = {text = "OSNL-0.2"}, and the repository's licence field reports NOASSERTION. The LICENSE file at the repository root is the authoritative text, and the README does not explain the terms.

## Sources

- [Issues](https://github.com/peteromallet/desloppify/issues)
- [peteromallet/desloppify on GitHub](https://github.com/peteromallet/desloppify)
- [Project website](https://desloppify.it/)
- [README](https://github.com/peteromallet/desloppify/blob/main/README.md)
- [Releases](https://github.com/peteromallet/desloppify/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/peteromallet-desloppify
