Docs chevron_right Configuration and Reports — .deslop.toml, flags, output

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>, // @generated or # 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 one report_hide pattern 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-language flag 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-level using the standard tracing filter 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 optional in the schema may be absent. Fields marked required are always present.
  • Clones are sorted by mass descending, 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.