文档 chevron_right 配置与报告 — .deslop.toml、命令行参数、JSON 输出

配置与报告

Deslop 有两种配置方式:

  • .deslop.toml — 与代码放在一起、并纳入版本管理的文件。项目级策略写在这里:跳过什么、隐藏什么、何时让 CI 失败。
  • 命令行参数 — 单次运行的覆盖项。参数始终优先于对应的配置键。

本页是一份参考手册。需要循序渐进的教程,请从快速开始读起。

.deslop.toml 文件

文件位置

Deslop 会在扫描根目录(你指向的目录)旁查找 .deslop.toml。用 --config 可以指向别处:

deslop .                          # 若存在则使用 ./.deslop.toml
deslop ./src --config ci.toml     # 显式指定配置,文件名任意

缺少配置文件不算错误 — Deslop 会按下文的内置规则运行。

格式

TOML 格式:一个共享的 [defaults] 区段,外加可选的按语言覆盖区段和四个行为区段。模式是带 gitignore 语义的 glob 模式相对扫描根目录解析 — 无论仓库在磁盘上位于何处,subdir/** 都匹配 <扫描根>/subdir/...

excludereport_hide — 核心区别

这两层做的事不同,区别很关键:

会发生什么 适用于
exclude 文件在分析前被丢弃 — 既不解析,也不指纹化。 第三方代码、构建产物,以及任何你永远不想参与比较的内容。
report_hide 文件会被分析,但每处出现都被标记为隐藏。只有当一个簇的全部成员都是隐藏的时,它才会从报告中移除。 生成的代码。你仍然想知道何时手写代码与某个生成文件重复了。

这种不对称是刻意的。report_hide 让"生成代码与自身重复"不进入头条,同时仍然暴露"你的代码与生成代码重复"的情况 — 因为那个簇至少有一个非隐藏成员,所以它会保留下来。

[defaults]

应用于每个文件,与语言无关。

[defaults]
exclude = [
  "**/node_modules/**",
  "**/dist/**",
  "vendor/**",
]
report_hide = [
  "**/*.snapshot.cs",
]

[defaults.boilerplate]
imports = "report"
类型 默认值 含义
exclude glob 列表 [] 从发现阶段丢弃的文件。与内置 exclude 叠加。
report_hide glob 列表 [] 会被分析但从渲染报告中隐藏的文件。与内置隐藏规则叠加。
boilerplate.imports "suppress" | "report" "suppress" 如何处理仅由 import / 序言构成的克隆。suppress 静默丢弃;report 仍抑制克隆告警,但发出一条低严重度的整洁度提示。

[language.<name>]

按语言的覆盖区段。名称是解析器的语言 id:csharprustpythondartjavascripttypescripttsxphpfsharpgo。这里的模式扩展默认值 — 永不替换 — 因此一个 .cs 文件会同时针对 defaults.exclude 加上 language.csharp.exclude 进行测试。

[language.csharp]
report_hide = ["**/Migrations/**"]

[language.python]
exclude = ["**/conftest.py"]

[language.dart.boilerplate]
imports = "report"

每个覆盖区段都接受与 [defaults] 相同的三个键:excludereport_hideboilerplate.imports

[threshold] — CI 门禁

[threshold]
max_duplication_percent = 20
类型 范围 含义
max_duplication_percent 浮点数 0.0100.0 当全仓库的 duplication_percent 超过此值时,deslop3 退出并使 CI 失败。

这是唯一需要显式启用的失败路径。没有 [threshold] 区段(且没有 --fail-over)时,无论发现多少重复,deslop 始终以 0 退出。命令行 --fail-over 参数会覆盖此键;--no-fail-over 会在单次运行中清除它。参见下文的退出码

[analysis]

[analysis]
allow_cross_language_comparison = false
类型 默认值 含义
allow_cross_language_comparison 布尔 false true 时,候选克隆对可以跨越不同语言。默认关闭,使报告聚焦于同语言重构。

[report]

[report]
split_by_language = false
类型 默认值 含义
split_by_language 布尔 false 将 HTML 报告按语言划分为各自的区段。--split-by-language 参数也能开启它 — 任一来源都会生效。

[ranking]

控制两类可降权克隆的评分方式。数据克隆是近乎逐字的数据块(长字面量表、夹具)。仅结构簇仅在形状上匹配,没有词法或语义支撑。两者都默认 demote,从而沉到真正的全证据克隆之下,但不消失。

[ranking]
data_clones = "demote"
data_clone_weight = 0.15
structural_only = "demote"
structural_only_weight = 0.15
类型 默认值 含义
data_clones "demote" | "ignore" | "keep" "demote" 数据类簇的策略。demote 降权,ignore 从报告中丢弃,keep 以全权重排名。
data_clone_weight 浮点数 0.15 data_clones = "demote" 时所乘的系数。必须在 (0.0, 1.0] 内。
structural_only "demote" | "ignore" | "keep" "demote" 对仅结构簇的同一套三选一策略。
structural_only_weight 浮点数 0.15 structural_only = "demote" 时所乘的系数。必须在 (0.0, 1.0] 内。

系数 0.0 会被拒绝(被降权的簇绝不能被静默抹除),大于 1.0 的值同样被拒绝(那会提升被降权的类别)。VS Code 扩展可以从其设置覆盖 structural_only;编辑器设置优先于文件。

内置规则(始终生效)

无论你如何配置,这些规则都会运行且无法关闭 — 它们让依赖树和机器生成代码远离每一份报告。

始终排除(路径中包含以下任一目录组件的文件):

node_modules   target   dist   build   .venv   __pycache__
.cargo   .git   .claude   .dart_tool   .pub-cache

始终从报告中隐藏(会被分析,但不进入头条):

  • 目录组件 generated/
  • 相邻组件对 alembic/versionstest/fixturestests/fixtures — 除非该对本身就是你的扫描根,从而让你能够刻意分析一个夹具语料库。
  • 文件后缀:.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
  • 任何在前一千字节内带有生成代码横幅的文件:@generatedGENERATED CODEauto-generatedautogeneratedautomatically generated(不区分大小写匹配)。

一个完整示例

# 在各处跳过第三方代码和构建产物。
[defaults]
exclude = [
  "**/node_modules/**",
  "**/dist/**",
  "third_party/**",
]
report_hide = [
  "**/*.snapshot.cs",
]

# 对仅由 import 构成的克隆给出整洁度提示,而非静默丢弃。
[defaults.boilerplate]
imports = "report"

# C# 数据库迁移是生成的 — 分析但隐藏它们。
[language.csharp]
report_hide = ["**/Migrations/**"]

# 全仓库重复超过 20% 时让 CI 失败。
[threshold]
max_duplication_percent = 20

# 将 HTML 报告按语言拆分。
[report]
split_by_language = true

# 直接丢弃纯仅结构匹配,而不是降权。
[ranking]
structural_only = "ignore"

命令行参数

每次运行也接受参数。在该次运行中,参数会覆盖对应的 .deslop.toml 键。

参数 默认值 含义
PATH . 要分析的目录(位置参数)。
--min-nodes <N> 30 构成克隆候选的最小 AST 子树节点数。越大 = 克隆越少、越大。
--output <PREFIX> deslop-report 报告的基路径;会追加 .json / .txt / .html
--config <FILE> <root>/.deslop.toml 显式配置文件。
--split-by-language 每种语言一个 HTML 区段(等同于 [report] split_by_language)。
--nojson / --notext / --nohtml 全开 抑制单一输出格式。至少需保留一种。
--fail-over <PERCENT> 重复超过 PERCENT 时以 3 退出。覆盖 [threshold]
--no-fail-over 在该次运行中清除任何阈值;绝不以 3 退出。
--technical 在 stderr 上显示研究者视图(分类 id、信号字母、节点数)。

嵌入

混合嵌入层默认关闭;Deslop 不依赖它即可提供结构与词法信号。在你有可用提供方时再开启。

参数 默认值 含义
--embeddings <MODE> off off 跳过嵌入;auto 探测提供方,不可用时带告警回退;required 在提供方不可达时硬失败。
--embedding-provider <ID> ollama 提供方注册键。目前仅实现了 ollama
--embedding-model <MODEL> nomic-embed-text 提供方所理解的模型 id(对 Ollama 即 ollama list 中的名称)。
--embedding-endpoint <URL> http://127.0.0.1:11434 提供方端点。

日志与颜色

参数 默认值 含义
--log-to-console 将日志发往 stderr,而非带时间戳的 deslop-<timestamp>.log 文件。
--log-level <LEVEL> info error | warn | info | debug | trace。被 RUST_LOG 覆盖。
--no-color 关闭 stderr 前导与摘要中的颜色。
--no-incremental 关闭 <root>/.deslop/cache/ 下的磁盘指纹缓存。该缓存默认开启,未改动的文件在下次运行时会跳过解析;传入此参数可让被扫描的目录保持原样。

开发与模拟参数

这些参数绕过或重放流水线,不属于常规使用:--from-report <FILE> 将已有的 JSON 报告重新渲染为 .txt/.html--debug-ast <FILE> 打印单个文件的规范化 AST 后退出;--rerun-touch--rerun-remove--rerun-add 通过增量会话重放文件变更,以演练实时更新路径。

优先级与环境

当同一项设置来自多处时,由谁胜出如下:

  • 阈值: --no-fail-over--fail-over <n>[threshold] max_duplication_percent → 关闭。
  • 按语言拆分: --split-by-language 参数 [report] split_by_language — 任一项都会启用。
  • 排名(structural_only): VS Code 扩展设置覆盖 .deslop.toml

另有两个环境变量同样生效:

  • RUST_LOG — 以标准 tracing 过滤器语法覆盖 --log-level
  • NO_COLOR — 即使没有 --no-color 也关闭颜色。当 stderr 不是终端时也会自动抑制颜色。

报告输出

每次运行都会生成三种报告。JSON 才是产品本身;文本和 HTML 是基于同一份数据的渲染器。TXT 或 HTML 中不会出现任何 JSON 里没有的结论。

JSON — 规范格式

deslop-report.json 是智能体读取的内容,也是 schema 消费方应当解析的内容。逐字段的完整结构参见面向 AI → 读取 JSON

保证:

  • schema 中标记为 optional 的字段可能缺失。标记为 required 的字段始终存在。
  • 簇按 weight 降序排序。clusters[0] 始终是最严重的重复。
  • UTF-8。无 BOM。LF 行尾。

TXT — 终端格式

deslop-report.txt 是 ASCII、按行组织、刻意保持朴素的格式。没有 ANSI 颜色,没有 Unicode 制表符绘图,没有分页转义码。可以无意外地通过管道送入 headgrepawk

deslop 0.0.0-dev -- 840 file(s), 142 cluster(s), 0 hidden
repo: 2.6% duplicated (48120 / 1832044 LOC, 142 clusters across 318 files)
embeddings: off
-- action hints --
  [identical] Extract the shared code into one definition and call it from every duplicate site.
#1 [0362505641efe3c7] weight=1252.80 size=3 nodes=58
  3 near-identical copies — safe to extract.
  :: Nearly identical across UserRepository.cs, ProductRepository.cs, OrderRepository.cs.

每个簇都是一个带编号的代码块 — #1 是最严重的重复 — 包含其权重、大小和节点数,随后是一段通俗易懂的摘要和一行解读。簇按最严重者优先列出。这种格式可以在每一种终端、每一次 SSH 会话和每一份 CI 日志中保持完好。

HTML — 可移植格式

deslop-report.html 是单个文件。所有 CSS 均已内联。无网络请求。把它放进 CI 工件、用邮件发送、在飞机上打开 — 它都能渲染。

HTML 渲染器使用与 JSON 和 TXT 相同的排名和相同的簇摘要。它额外提供:

  • 语法高亮的示例代码片段,过长的片段和额外的出现位置会折叠进可展开的开关中
  • 每个重复组上的「AI 匹配」徽章和影响力标签
  • 在可折叠的「运行详情」页脚中,每组一张信号表(结构 / token / 嵌入 / 融合)

它不会额外提供:JSON 中没有的评分、超出 summary 字段的评论,或指向外部服务的链接。

文件写到哪里

Deslop 写出的一切都会进入被扫描项目根目录下的单个 .deslop/ 目录,或你通过 --output 传入的前缀。若只想生成 JSON 报告,请抑制另外两种格式:

deslop . --notext --nohtml      # writes .deslop/deslop-report.json

诊断信息与报告是分开的。默认情况下,诊断信息通过带结构化字段的 tracing 写入一个带时间戳的日志文件(.deslop/logs/deslop-<timestamp>.log);传入 --log-to-console 可改为将其发送到 stderr。人类可读的前言和摘要始终输出到 stderr

退出码

退出码 含义
0 已完成。重复低于阈值,或未设置阈值。
1 运行时错误 — 扫描路径不存在、分析失败、I/O 错误,或一个标记为 required 的嵌入提供方无法访问。绝不会是 panic。
2 用法错误 — 未知标志,或超出范围 / 非有限的阈值,在运行开始前即被拒绝。
3 阈值被突破。 完整报告仍会写入磁盘,以便呈现这些重复。

deslop 绝不会因用户输入而 panic。失败通过这些退出码和 stderr 上的结构化错误呈现。

退出码 3CI 门禁。它只在你主动启用时才会触发 — 通过传入 --fail-over <percent>,或设置 [threshold] max_duplication_percent。详见基于重复阈值设置 CI 门禁的演练,或直接使用 GitHub Action