npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dsh-similarity

v0.1.0

Published

Similarity toolkit for DeepSeek Harness: edit distance (Levenshtein / Dice / Jaccard / Hamming), near-duplicate pair discovery across a list, and fuzzy best-match lookup. Zero runtime dependencies, read-only.

Readme

dsh-similarity

DeepSeek Harness 的相似度工具箱:edit_distance 比较两段文本的距离与相似度,similar_pairs 在一组条目里找出所有近似重复的对,best_match 为一段输入从候选列表中挑出最接近的若干条。支持 levenshtein / dice / jaccard / hamming 四种算法。只读、零运行时依赖。

  • 源码:https://github.com/geeklei/dsh-plugins/tree/main/dsh-similarity
  • npm:https://www.npmjs.com/package/dsh-similarity

功能说明

「看着一样却不相等」「到底是同一个还是两个」「用户输入的枚举值拼错了」——这类判断凭肉眼很容易出错:差一个字符、多一个空格、大小写不同,结果就完全不同。本插件把「有多像」变成一个可比较的数字。

edit_distance —— 两段文本的相似度

算法:levenshtein(区分大小写;保留空白)
左侧:8 字符;右侧:8 字符
距离:1
相似度:0.8750(87.5%)

编辑操作:需要 1 次插入 / 删除 / 替换即可把左侧变成右侧。
口径:按 Unicode 码点比较(emoji 记 1 个字符);相似度 = 1 - 距离 / 较长一侧长度。

四种算法的口径不同,按场景选择:

| 算法 | 距离含义 | 相似度口径 | 适合 | |---|---|---|---| | levenshtein(默认) | 插入 / 删除 / 替换的最少次数 | 1 - 距离 / 较长一侧长度 | 错别字、少字多字、拼写纠错 | | dice | 1 - 相似度 | bigram 多重集合的 Dice 系数 | 中文长文本、名称、句子的近似度 | | jaccard | 1 - 相似度 | bigram 去重集合的交并比 | 关注「出现过哪些片段」而非次数 | | hamming | 等长逐位不同的位数 | 1 - 距离 / 长度 | 定长编码、版本号、等长 ID |

算法:jaccard(区分大小写;保留空白)
左侧:15 字符;右侧:15 字符
距离:0.0714
相似度:0.9286(92.9%)

说明:dice / jaccard 无编辑距离概念,距离按 1 - 相似度 给出。
口径:按 Unicode 码点切成 bigram,Jaccard 相似度 = 交集 / 并集(bigram 去重)。

ignore_case(忽略大小写)与 ignore_whitespace(忽略全部空白)只管比较口径,不改动原文本:苹果手机 iPhone 15 Pro 与 苹果手机 iphone 15 pro 在 ignore_case=true 时相似度为 1.0000。

similar_pairs —— 在一组条目里找近似重复

来源:(内联文本)
条目:5 条(空行已跳过)
算法:dice;阈值 ≥ 0.5;区分大小写;保留空白;含完全相同

比较:10 对,命中 3 对(阈值 ≥ 0.5)

|   # | 相似度 | 条目 A                          | 条目 B                        |
| --- | ------ | ------------------------------- | ----------------------------- |
|   1 | 0.9600 | #0 深圳市深智城科技发展有限公司 | #1 深圳市深智城科技发展有限公 |
|   2 | 0.6957 | #0 深圳市深智城科技发展有限公司 | #2 深圳深智城科技有限公司     |
|   3 | 0.6364 | #1 深圳市深智城科技发展有限公   | #2 深圳深智城科技有限公司     |

条目按行给出(内联 text 或 path 指向的文件),空行自动跳过,每行两端空白会被裁掉。列表会被两两比较(n × (n-1) / 2 对),结果按相似度降序返回 Top N。

include_identical 默认 true,完全相同的重复项也会被列出(找重复数据时通常正是想要的);只想看「不完全相同但很像」的对时设为 false。

best_match —— 从候选里挑最像的

查询:深智城(3 字符)
候选来源:(内联文本)
候选:3 条(空行已跳过)
算法:dice;阈值 ≥ 0.2;区分大小写;保留空白

命中:1 条(阈值 ≥ 0.2)

|   # | 相似度 |   距离 | 候选                         |
| --- | ------ | ------ | ---------------------------- |
|   1 | 0.2667 | 0.7333 | 深圳市深智城科技发展有限公司 |

最佳匹配:深圳市深智城科技发展有限公司(相似度 0.2667)

没有任何候选达到阈值时,会给出「最接近的一条」作为参考,而不是只说「没找到」,方便判断是拼写问题还是真的没有对应值。

安装

npm install dsh-similarity

安装后在 cordis.patch.yml 中加入:

- insert:
    - id: similarity
      name: "dsh-similarity"

使用说明

edit_distance

| 参数 | 类型 | 说明 | |---|---|---| | left | string | 左侧文本(必填) | | right | string | 右侧文本(必填) | | algorithm | string | levenshtein(默认)| dice | jaccard | hamming | | ignore_case | boolean | 忽略大小写,默认 false | | ignore_whitespace | boolean | 忽略全部空白字符,默认 false | | max_chars | number | 输出字符上限,默认 20000,上限 200000 |

用户:这两个订单号差了几个字符

edit_distance(left="订单A-1001", right="订单A-1002")

用户:这两段文案是不是只差大小写

edit_distance(left="苹果手机 iPhone 15 Pro", right="苹果手机 iphone 15 pro", algorithm="dice", ignore_case=true)

similar_pairs

| 参数 | 类型 | 说明 | |---|---|---| | path | string | 条目文件路径,每行一条(与 text 二选一) | | text | string | 内联条目列表,每行一条(优先于 path) | | algorithm | string | 同上,默认 dice | | threshold | number | 相似度阈值,0~1,默认 0.6 | | limit | number | 最多返回对数,默认 20,上限 500 | | include_identical | boolean | 是否把完全相同的条目也计入,默认 true | | ignore_case / ignore_whitespace | boolean | 同上 | | max_items | number | 最多比较条目数,默认 1000,上限 5000 | | max_chars / max_size | number | 输出字符上限 / 文件大小上限 |

用户:这份客户名单里有没有重复或很像的名字

similar_pairs(path="customers.txt", threshold=0.8, limit=30)

用户:只看近似重复,不要完全一样的

similar_pairs(path="customers.txt", include_identical=false, threshold=0.7)

best_match

| 参数 | 类型 | 说明 | |---|---|---| | query | string | 查询文本(必填) | | path / text | string | 候选列表,每行一条(二选一,text 优先) | | algorithm | string | 同上,默认 dice | | threshold | number | 相似度阈值,默认 0.3 | | limit | number | 最多返回候选数,默认 5,上限 500 | | ignore_case / ignore_whitespace | boolean | 同上 | | max_items / max_chars / max_size | number | 同上 |

用户:用户输入的城市名拼错了,帮我匹配到规范名称

best_match(query="深洲市", path="cities.txt", threshold=0.4)

用户:这几个状态值里哪个最接近 "pendding"

best_match(query="pendding", text="pending\npaid\ncancelled\nrefunded", ignore_case=true)

典型场景

  • 数据去重:对导入的客户名、公司名、地址做 similar_pairs,先看相似度最高的几对,再人工确认是否同一实体。
  • 拼写纠错:用 best_match 把用户输入或 OCR 结果映射到已有枚举值、字典项。
  • 变更核对:用 edit_distance 判断两个版本号、两条配置、两行日志是「同一条」还是「改动过的」。
  • 近似搜索:把候选集交给 best_match,用相似度排序代替严格相等匹配。
  • 模糊查重:标题、摘要、商品名这类重复但不完全一致的场景,用 dice 更稳。
  • 定长编码比对:hamming 直接给出等长串(哈希前缀、序列号)差异位数。
  • 大小写/空格脏数据:先用 ignore_case、ignore_whitespace 归一,避免格式差异淹没真实差异。

安全边界

  • 只读:三个工具都不写入、不修改、不删除文件;测试含源文件 size/mtime 不变的只读性断言。
  • 不访问网络:比较全部在本地完成,无任何外部请求。
  • 不执行输入:只做字符串比较,不做 eval、不解析表达式、不加载模块、不跑正则回溯。
  • 二进制拒绝:读取时检测 NUL 字节,疑似二进制文件直接拒绝解析。
  • 多层上限:文件大小上限 51200 KB、输出字符上限 200000、返回对数上限 500;similar_pairs 两两比较的条目数上限 5000(默认 1000),避免平方级增长失控。
  • 编辑距离长度上限:levenshtein / hamming 单侧上限 5000 字符,超限直接报错并建议改用 dice / jaccard(线性复杂度)。
  • 不脱敏:条目原样输出,长条目在表格中按显示宽度截断(仅影响展示,不影响计算)。若数据含密钥等敏感信息,请先用 dsh-credential-scan 检查。

其他插件集成说明

| 协作插件 | 集成方式 | |---|---| | dsh-csv-explorer | 先预览、筛选出目标列,再导出成一列条目交给 similar_pairs | | dsh-line-ops | 用行操作去重、排序、截取后再做相似度比较 | | dsh-text-diff | text-diff 给出「差异在哪」,similarity 给出「差多少」,配合判断改动幅度 | | dsh-text-stats | 先用文本统计了解条目规模,再决定 threshold 与 limit | | dsh-unicode-toolkit | 比较结果异常时,用 inspect_codepoints 排查零宽字符、全半角导致的「看着一样却不相等」 | | dsh-json-yaml-toolkit | 从 JSON / YAML 中取出字段值列表,再做模糊匹配 | | dsh-todo-audit | 对 TODO 文本做近似聚类,找出重复登记的待办 | | dsh-workspace-journal | 对日记条目做近似检测,避免重复记录同一件事 | | dsh-credential-scan | 处理前扫描数据中是否混入凭据、密钥 |

版本说明

  • 0.1.0(2026-10-02):初始版本。
    • edit_distance:levenshtein / dice / jaccard / hamming 四种算法,输出距离与相似度百分比,支持忽略大小写与空白
    • similar_pairs:条目两两比较,按阈值过滤、按相似度排序返回 Top N,可选择是否计入完全相同项
    • best_match:为查询文本返回 Top N 候选及分数,无命中时给出最接近项
    • 全部按 Unicode 码点比较(emoji 记 1 个字符);表格按显示宽度对齐(中日韩字符按 2 列宽)
    • 二进制拒绝、多层上限与平方级比较的条目数保护

RoadMap

v0.2(计划)

  • similar_pairs 增加 cluster:对命中的相似对做并查集聚类,直接输出「疑似重复分组」
  • edit_distance 增加 damerau(允许相邻换位)与 token(按词而非字符比较)
  • best_match 增加 all_matches:返回所有超过阈值的候选并标注并列
  • 新增 normalize_key:统一的键归一化(大小写、全半角、空白、标点)供三个工具共用

v0.3(展望)

  • similar_pairs 增加 minhashes 近似分桶,支持十万级条目的快速查重
  • 词级权重与停用词支持,提升中文长句的比较质量
  • 输出 JSON 结构,便于管道化处理
  • 与 dsh-import-graph、dsh-deps-audit 联动,检测项目中近似重复的模块名与依赖名

FAQ

Q:为什么默认用 dice 而不是 levenshtein? similar_pairs 和 best_match 面向的是「成组条目」的比较,中文长度普遍偏长,编辑距离对少数字符差异过于敏感;bigram 系数更贴近人的直觉。单对短字符串比较用 edit_distance 的默认 levenshtein 即可。

Q:为什么 similar_pairs 的算法默认和 edit_distance 不一样? 同上:前者用于成组近似查重,后者用于两段文本的精确差异度量,默认值按各自最常用场景设定,都可显式覆盖。

Q:相似度 0.93 算不算重复? 没有通用阈值。短字符串(小于 6 个字符)建议 0.8 以上,中文长名称 0.6~0.75 往往就已经是重复了。建议先用 threshold=0.5 看一轮分布,再决定卡在哪。

Q:hamming 遇到长度不同的两侧会怎样? edit_distance 直接报错并给出两侧长度;similar_pairs 与 best_match 跳过长度不匹配的组合,并在输出中提示跳过了多少条。

Q:为什么 best_match 有时候返回 0.2667 这种很低的分数? 分数低说明两者只是部分片段相同。best_match 会如实给出分数,不会因为「它是第一名」就抬高它;是否需要采用由阈值决定。

Q:similar_pairs 条目很多会不会很慢? 比较量随条目数平方增长。默认上限 1000 条(约 50 万次比较),超过会被截断并提示(返回结果会标注「已忽略」)。条目多时先降低 max_items、提高 threshold,或先用其他工具把范围缩小。

Q:ignore_whitespace 会把换行也去掉吗? 会。该项会移除全部空白字符(空格、制表符、换行)。条目本身按行拆分后再比较,行内的空白差异才会被该项影响。

Q:为什么 emoji 算 1 个字符? 按 Unicode 码点切分,而不按 UTF-16 单元,所以 😀 记为 1 个字符。这对中日韩与表情混排的文本更符合直觉。

Q:能直接改文件里的条目吗? 不能。本插件只读,输出的是分析结果,不写回文件。

源码与发布地址

  • 源码:https://github.com/geeklei/dsh-plugins/tree/main/dsh-similarity
  • npm:https://www.npmjs.com/package/dsh-similarity

许可证

MIT