配置与报告
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/...。
exclude 与 report_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:csharp、rust、python、dart、javascript、typescript、tsx、php、fsharp 或 go。这里的模式扩展默认值 — 永不替换 — 因此一个 .cs 文件会同时针对 defaults.exclude 加上 language.csharp.exclude 进行测试。
[language.csharp]
report_hide = ["**/Migrations/**"]
[language.python]
exclude = ["**/conftest.py"]
[language.dart.boilerplate]
imports = "report"
每个覆盖区段都接受与 [defaults] 相同的三个键:exclude、report_hide 和 boilerplate.imports。
[threshold] — CI 门禁
[threshold]
max_duplication_percent = 20
| 键 | 类型 | 范围 | 含义 |
|---|---|---|---|
max_duplication_percent |
浮点数 | 0.0–100.0 |
当全仓库的 duplication_percent 超过此值时,deslop 以 3 退出并使 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/versions、test/fixtures、tests/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。 - 任何在前一千字节内带有生成代码横幅的文件:
@generated、GENERATED CODE、auto-generated、autogenerated或automatically 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 制表符绘图,没有分页转义码。可以无意外地通过管道送入 head、grep、awk。
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 上的结构化错误呈现。
退出码 3 是 CI 门禁。它只在你主动启用时才会触发 — 通过传入 --fail-over <percent>,或设置 [threshold] max_duplication_percent。详见基于重复阈值设置 CI 门禁的演练,或直接使用 GitHub Action。