Configuration and Reports
This reference covers 0.35.0.
.deslop.toml— a committed file next to your code. This is where project-wide policy lives: what to skip, what to hide, and when to fail CI.- CLI flags — per-run overrides. A flag always wins over the matching config key.
Everything on this page is a reference. For a guided walkthrough, start with Getting Started.
The .deslop.toml file
Where it lives
Deslop looks for .deslop.toml next to the scan root — the directory you point it at. Point it elsewhere with --config:
deslop . # uses ./.deslop.toml if present
deslop ./src --config ci.toml # explicit config, any name
A missing config is not an error — Deslop runs with the built-in rules below.
Format
TOML, with one shared [defaults] block plus optional per-language overlays and four behaviour sections. Patterns are glob patterns with gitignore semantics, resolved relative to the scan root — subdir/** matches <scan-root>/subdir/... regardless of where the repo sits on disk.
exclude vs report_hide — the core idea
Two tiers do different jobs, and the difference matters:
| What happens | Use it for | |
|---|---|---|
exclude |
The file is dropped before analysis — never parsed, never fingerprinted. | Vendored code, build output, anything you never want compared. |
report_hide |
The file is analysed, but every occurrence is marked hidden. A cluster is dropped from the report only when all of its members are hidden. | Generated code. You still want to know when hand-written code duplicates a generated file. |
The asymmetry is deliberate. report_hide keeps "generated code duplicates itself" out of your headline while still surfacing "your code duplicates generated code" — because that cluster has at least one non-hidden member, so it survives.
[defaults]
Applied to every file regardless of language.
[defaults]
exclude = [
"**/node_modules/**",
"**/dist/**",
"vendor/**",
]
report_hide = [
"**/*.snapshot.cs",
]
[defaults.boilerplate]
imports = "report"
| Key | Type | Default | Meaning |
|---|---|---|---|
exclude |
list of globs | [] |
Files dropped from discovery. Additive with the built-in excludes. |
report_hide |
list of globs | [] |
Files analysed but hidden from the rendered report. Additive with the built-in hide rules. |
boilerplate.imports |
"suppress" | "report" |
"suppress" |
How import / prologue-only clones are handled. suppress drops them silently; report still suppresses the clone warning but emits a low-severity hygiene hint. |
[language.<name>]
Per-language overlays. The name is the parser's language id: csharp, rust, python, dart, javascript, typescript, tsx, php, fsharp, or go. Patterns here extend the defaults — they never replace them — so a .cs file is tested against defaults.exclude plus language.csharp.exclude.
[language.csharp]
report_hide = ["**/Migrations/**"]
[language.python]
exclude = ["**/conftest.py"]
[language.dart.boilerplate]
imports = "report"
Each overlay accepts the same three keys as [defaults]: exclude, report_hide, and boilerplate.imports.
[threshold] — the CI gate
[threshold]
max_duplication_percent = 20
| Key | Type | Range | Meaning |
|---|---|---|---|
max_duplication_percent |
float | 0.0–100.0 |
When repo-wide duplication_percent exceeds this, deslop exits 3 and fails CI. |
This is the only opt-in failure path. With no [threshold] block (and no --fail-over), deslop always exits 0 no matter how much duplication it finds. The CLI --fail-over flag overrides this key; --no-fail-over clears it for a single run. See Exit codes below.
[analysis]
[analysis]
allow_cross_language_comparison = false
include_dependencies = false
incremental = true
| Key | Type | Default | Meaning |
|---|---|---|---|
allow_cross_language_comparison |
bool | false |
When true, candidate clone pairs may span different languages. Off by default, so reports stay focused on same-language refactoring. |
include_dependencies |
bool | false |
Analyse dependency trees too. Off by default: worst-first ranking would otherwise put duplication you cannot edit above your own. |
incremental |
bool | true |
Reuse the on-disk parse cache. Set false to force a full re-parse on every run, on every surface — CLI, editor, and agent alike. |
max_average_line_bytes |
integer | 200 |
Average-line-length ceiling used to identify large minified build artifacts. |
[report]
[report]
split_by_language = false
| Key | Type | Default | Meaning |
|---|---|---|---|
split_by_language |
bool | false |
Divide the HTML report into one section per language. Also enabled by the --split-by-language flag — either source turns it on. |
[ranking]
Clones are ranked by duplicated mass: canonical AST nodes × additional visible copies. Category and confidence do not multiply that mass.
To hide informational shape-only findings:
[ranking]
structural_only = "ignore"
structural_only accepts demote (default), ignore or keep. Visible shape-only findings remain after clones, with zero duplicated mass; none of these settings makes them count as duplication. The VS Code setting overrides this file setting. data_clones = "ignore" excludes data-category findings before ranking.
Built-in rules (always on)
Minified and bundled files are also excluded by recognized filename suffixes and a configurable average-line-length check. Large readable source files remain eligible. Generated-file banners are read from parsed comments, so a marker inside a string is not a banner.
These rules apply before user patterns. Exclusions remove files from analysis; report-hide rules retain analysis but hide occurrences.
Build output and tool caches, excluded with no way to opt back in — none of it is source you wrote:
target dist build __pycache__ .dart_tool .git .claude
Dependency trees, excluded unless you set [analysis] include_dependencies = true:
node_modules vendor .cargo .pub-cache .venv
Always hidden from the report (analysed, but kept out of the headline):
- The directory component
generated/. - The adjacent component pairs
alembic/versions,test/fixtures,tests/fixtures— unless that pair is your scan root, so you can still analyse a fixture corpus on purpose. - File suffixes:
.g.cs,.generated.cs,.designer.cs,.pb.cs,.openapi.cs,.generated.py,_generated.py,.g.dart,.freezed.dart,.gr.dart,.config.dart,.gen.dart,.mocks.dart,.pb.dart,.pbenum.dart,.pbjson.dart,.pbserver.dart,.pbgrpc.dart. - Any file that opens with a generator's banner — a whole comment line above the first code, such as
// GENERATED CODE - DO NOT MODIFY BY HAND,// Code generated … DO NOT EDIT.,// <auto-generated>,// @generatedor# Generated by the protocol buffer compiler. DO NOT EDIT!. A phrase inside a sentence, a string, or a comment further down never counts, so a hand-written file that merely mentions generators stays in the report. The full banner list is in the spec; a generator that is not on it is onereport_hidepattern away.
A complete example
# Skip vendored and build output everywhere.
[defaults]
exclude = [
"**/node_modules/**",
"**/dist/**",
"third_party/**",
]
report_hide = [
"**/*.snapshot.cs",
]
# Surface a hygiene hint instead of silently dropping import-only clones.
[defaults.boilerplate]
imports = "report"
# C# database migrations are generated — analyse but hide them.
[language.csharp]
report_hide = ["**/Migrations/**"]
# Fail CI when repo-wide duplication climbs above 20%.
[threshold]
max_duplication_percent = 20
# Split the HTML report per language.
[report]
split_by_language = true
# Hide informational shape-only matches.
[ranking]
structural_only = "ignore"
CLI flags
Every run also takes flags. A flag overrides the matching .deslop.toml key for that run.
| Flag | Default | Meaning |
|---|---|---|
PATH |
. |
Directory to analyse (positional). |
--min-nodes <N> |
30 |
Minimum AST subtree node count for a clone candidate. Higher = fewer, larger clones. |
--output <PREFIX> |
.deslop/deslop-report |
Base path for reports; .json / .txt / .html are appended. Logs follow into <dir>/logs/. |
--config <FILE> |
<root>/.deslop.toml |
Explicit config file. |
--split-by-language |
off | One HTML section per language (same as [report] split_by_language). |
--nojson / --notext / --nohtml |
all on | Suppress a single output format. At least one must remain. |
--fail-over <PERCENT> |
— | Exit 3 when duplication exceeds PERCENT. Overrides [threshold]. |
--no-fail-over |
— | Clear any threshold for this run; never exit 3. |
--technical |
off | Show the researcher view (taxonomy IDs, signal letters, node counts) on stderr. |
--diff <FILE> |
— | Scope the report to a unified diff's added lines; - reads stdin. The scan still covers the whole tree. A diff that does not match the tree is refused. |
--only-changed |
off | Drop clusters that miss the diff, and gate --fail-over on the diff-scoped percentage so old debt cannot fail a pre-merge check. Requires --diff. |
Compare two occurrences
--compare was added in 0.35.0. Pass two endpoints as <path>:<start_byte>:<end_byte>, using exact byte ranges from the JSON report:
deslop . --compare "::" --compare "::"
The command scans the root, recomputes the pair's evidence, and prints a JSON verdict to stdout. Offsets are bytes, not lines, and the end is exclusive. A range must resolve to a fingerprinted occurrence; a cluster ID is not an endpoint.
Embeddings
The hybrid embedding layer is off by default; Deslop ships structural and token signals without it. Turn it on when you have a provider running.
| Flag | Default | Meaning |
|---|---|---|
--embeddings <MODE> |
off |
off skips embeddings; auto probes the provider and falls back with a warning; required hard-fails when the provider is unreachable. |
--embedding-provider <ID> |
ollama |
Provider registry key. Only ollama is implemented today. |
--embedding-model <MODEL> |
nomic-embed-text |
Model id as the provider understands it (for Ollama, the name from ollama list). |
--embedding-endpoint <URL> |
http://127.0.0.1:11434 |
Provider endpoint. |
Logging and colour
| Flag | Default | Meaning |
|---|---|---|
--log-to-console |
off | Send logs to stderr instead of a timestamped deslop-<timestamp>.log file. |
--log-level <LEVEL> |
info |
error | warn | info | debug | trace. Overridden by RUST_LOG. |
--no-color |
off | Disable colour in the stderr preamble and summary. |
--no-incremental |
off | Disable reads and writes for the on-disk fingerprint and embedding caches under <root>/.deslop/cache/. Reports and logs are still written normally. |
Developer and simulation flags
These bypass or replay the pipeline and are not part of normal use: --from-report <FILE> re-renders an existing JSON report into .txt/.html; --debug-ast <FILE> prints one file's normalised AST and exits; --rerun-touch, --rerun-remove, and --rerun-add replay file changes through an incremental session to exercise the live update path.
Precedence and environment
When the same setting comes from more than one place, this is who wins:
- Threshold:
--no-fail-over→--fail-over <n>→[threshold] max_duplication_percent→ off. - Split by language: the
--split-by-languageflag or[report] split_by_language— either one enables it. - Ranking (
structural_only): the VS Code extension setting overrides.deslop.toml.
Two environment variables also apply:
RUST_LOG— overrides--log-levelusing the standardtracingfilter syntax.NO_COLOR— disables colour even without--no-color. Colour is also suppressed automatically when stderr is not a terminal.
Report output
A normal scan emits JSON, text and HTML unless a format is disabled. The JSON is the product; the text and HTML are renderers over the same data. No claim appears in TXT or HTML that is not also present in the JSON.
JSON — canonical
deslop-report.json is what agents read and what schema consumers should parse. Its full shape, field by field, is in For AI → Read the JSON.
Guarantees:
- Fields marked
optionalin the schema may be absent. Fields markedrequiredare always present. - Clones are sorted by
massdescending, with stable IDs breaking ties. Informational findings follow clones and have no clone rank. - UTF-8. No BOM. LF line endings.
TXT — terminal
deslop-report.txt is a line-oriented report for terminals and CI logs. Clone entries show kind, whole-number mass, occurrence count and source locations in rank order. Pair similarity measurements are requested separately; the report does not claim an extraction is safe.
HTML — portable
deslop-report.html is a single file. All CSS is inlined. No network requests. Drop it into a CI artifact, email it, open it on an airplane — it renders.
The HTML renderer uses the same ranking and the same cluster summaries as JSON and TXT. It adds:
- syntax-highlighted example snippets, with long snippets and extra locations folded into collapsible toggles
- one collapsible group per clone kind, each card titled with that kind and carrying the group's mass
- run metadata — tool version, group counts, embedding provenance and the schema reference — in a collapsible "Run details" footer
It does not add: scores not in the JSON, commentary beyond the summary field, or links to external services.
Where the files land
Everything Deslop writes goes into one .deslop/ directory at the root of the scanned project, or the prefix you pass to --output. To emit only the JSON report, suppress the other two formats:
deslop . --notext --nohtml # writes .deslop/deslop-report.json
Diagnostics are separate from reports. By default they go to a timestamped log file (.deslop/logs/deslop-<timestamp>.log) via tracing with structured fields; pass --log-to-console to send them to stderr instead. The human-readable preamble and summary always go to stderr.
Exit codes
| Code | Meaning |
|---|---|
0 |
Completed. Duplication was below the threshold, or no threshold was set. |
1 |
Runtime error — nonexistent scan path, analysis failure, I/O error, or a required embedding provider that was unreachable. |
2 |
Usage error — unknown flag, or an out-of-range / non-finite threshold value, rejected before the run starts. |
3 |
Threshold breached. The full report is still written to disk so the offenders can be surfaced. |
Failures surface through these exit codes and a structured error on stderr.
Exit 3 is the CI gate. It only ever fires when you opt in — by passing --fail-over <percent> or setting [threshold] max_duplication_percent. See Gate CI on a duplication threshold for the walkthrough, or the GitHub Action for a ready-to-use CI job.