dsh-at-file-everything
Answers @file completion from the Everything index through es.exe instead of walking the workspace, keeping the workspace root as the ranking scope. Windows only; needs Everything and es.exe on PATH.
Install
dsh plugin --profile web add dsh-at-file-everything
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-at-file-everything
为 Windows 上的 DeepSeek Harness 提供 Everything 索引搜索,经 es.exe 查询:给人用的秒级 @文件 补全,以及给模型用的 find_files 工具——两者共用同一份 Everything 索引。
English documentation: [README.md](README.md)。
为什么需要它
有两个调用方都需要找文件,而遍历目录树对两者都不合适。
人敲 @ 的时候。 Harness 自带的 @deepseek-ai/dsh-file-reference-local 会自己遍历工作区,并按 maxEntries 截断。在大仓库里这种遍历既慢又有损。以本插件所针对的 Unity 工程为例:
· 上游遍历方案 · 本插件
检索对象 · 工作区根目录下的每个文件 · Everything 索引,用 -path 限定范围
条目上限 · maxEntries(默认 50 000) · 无
会话里第一次 @ · 完整遍历 · ~80 ms
后续查询 · 在有限索引里重排 · ~70–80 ms
覆盖范围 · 调优后约 46.9 万 / 约 150 万 · 整个限定范围
占用堆内存 · 索引约 99 MB · 插件不持有
上游上限造成的真实故障:Library/ 下约 120 万文件、Output/ 下约 27 万文件,在触达 Assets/ 源码之前就把索引额度耗尽,于是 @PlayablePrefab 只返回 Library/* 的噪声,真正的文件反而搜不到。
模型自己找路径的时候。 它只有 glob 和 shell,两者都遍历目录树,都会遇到同一个额度问题。find_files 改查索引——就是让 @ 变快的那次查询,放到模型能调用的地方。
Everything 本身就以 Windows 服务的形式维护着全机索引,因此两个入口都直接查它,不做任何遍历。
find_files 工具
参数 · 含义
query · 文件或目录名,或它的任意连续片段。大小写不敏感。* 或省略表示「范围内全部」。
path · 限定搜索的绝对目录,含其子目录。省略则搜索所有已索引磁盘。
foldersOnly / filesOnly · 限定返回条目类型。
limit · 分页大小(默认 50,最多 500)。
工具返回绝对路径,每行一条,并说明这一页与整个匹配集的关系:
2 match(es) under Y:\pm-devs: Y:\pm-devs\Assets\Scripts\Behaviour.Editor\ Y:\pm-devs\Assets\Scripts\Deep.ts (more than 2 matched; raise `limit`, or narrow `query`/`path`)
最后那行很重要:没有它,被截断的一页会被当成完整答案,模型就会把存在的文件报成找不到。只有当页取满时才加这行,所以短页确实意味着「这就是全部」。
它刻意不限定在工作区内。 @ 补全限定在一个工作目录,是因为补全框绝不能冒出用户看不见的、另一个副本里的文件。而被要求找文件的模型需求正好相反:在存着同一工程多份副本的机器上,限定范围只会让它答错副本或什么都答不到。它返回绝对路径,让模型自己决定读哪个。
它和补全共用 esClient 适配层,但不共用排序:补全为人的按键排序并返回相对路径;工具按要求返回全部匹配、绝对路径。各自服务各自的调用方,而不是折中。
只有当组合里有 tools 服务时才注册该工具,所以没有工具服务的组合依然有 @ 补全。
不需要任何配置
光有工具描述,模型并不会用它。它同时看到 glob、grep、find_files,会按习惯选——而习惯通常是 glob,那个遍历目录树、跑到一半就放弃的。所以插件还会注入一段 system prompt,讲清三者各自回答哪个问题:按名字定位文件(尤其是工作区外、或被忽略的大目录里)用 find_files;要路径模式且需遵守忽略规则用 glob;搜内容用 grep。
这段说明随工具注册一起安装,没有自己的开关。用户没有任何东西要打开:即使从没听说过这个工具,模型也会知道该调它。它紧挨在宿主自己的 glob 说明之前,让两者读起来是同一个决策,而不是两个互不相关的功能。
installPrompt 仍然存在,但它只控制 @path 那段说明——关掉它不会让模型看不到这个工具。
安装
# 每台机器装一次 winget install voidtools.Everything # 每个 profile 装一次 dsh plugin --profile <profile> add dsh-at-file-everything
es.exe 不是单独的前置条件:本包在 bin/ 里带了一份固定版本的副本,机器上没有自己的客户端时就用它。若你希望用自己的 ES(它跟你的 Everything 版本一致),装上并留在 PATH 上即可——PATH 上可用的 es.exe 永远优先于随包副本。
# 可选:用你自己的客户端,而不是随包副本 winget install voidtools.Everything.Cli
随包提供的 cordis.patch.yml 会:
1. 插入 file-reference-everything 行(即本插件);
2. 禁用 file-reference-local,保证 ctx.fileReferences 只有一个提供者。
之后重启该 Harness profile。组合中的其他部分无需改动:Session Controller 的 fileReferences/list Remote 与 Web 输入框仍然解析同一个服务。
发布包自带已构建的 lib/,不需要你本地先编译。
从本地代码库安装
开发阶段可以直接指向本仓库:
dsh plugin --profile <profile> add link:<本仓库路径>
更新link:安装时注意。 在本 profile 的 pnpm 设置下,link:依赖是被复制进去的,不是符号链接。因此只重新构建lib/不够——已安装的那份会继续跑旧代码,必须重新执行dsh plugin --profile <profile> add link:<本仓库路径>(或先删掉<profile>/node_modules/dsh-at-file-everything再装)。开发时很容易被这一点误导,以为改动没生效。
前置条件
- Windows。 插件声明
os: win32;其他平台上没有 Everything,也就没有答案来源。 - Everything 正在运行并已建立索引。 插件只查询它,不捆绑、不启动它。
排查前先确认环境:
es -get-everything-version # Everything 服务是否在应答 es -version # 命令行客户端是否可用
es.exe 从哪来
Everything 和它的命令行客户端是两个独立的下载:服务与索引是一个安装包,es.exe 是另一个。很多机器装了 Everything 用了好几年,却从来没装过客户端,所以本插件不假设它一定存在,而是按下面的顺序解析:
1. esCommand 写成绝对路径时——显式指定永远优先;
2. esCommand(默认 es.exe)按 extraPathDirectories、再按 PATH 查找;
3. 随包的 bin/es.exe。
候选存在但启动失败(比如配置的路径已被卸载)会继续往下一个候选走,代价只是一次失败的启动,而不是整个补全列表失效。
随包副本是兜底,不是覆盖。 它排在 PATH 之后是刻意的:装了 ES 的用户应该用自己那份——那才是与他 Everything 版本配对的客户端。随包副本只为「有服务、没客户端」的机器准备。
它固定为 ES 1.1.0.34,而不是最新版。从 1.1.0.35 起,-n 被实现成 count: 过滤,在 Everything 1.4 上每次查询要先多花约 300 ms 才返回第一行——而每一次按键都要付这笔钱。实测表格与推导见 [bin/README.md](bin/README.md);1.1.0.34 是最后一个既让 -n 便宜、又已支持 --、-get-result-count 以及 Everything 1.5 的 IPC3 的版本。
那张表是按本插件的参数向量测的,所以如果改动发出哪些开关,值得重测。
如果某个部署需要指定客户端——离线镜像、受限机器——把 esCommand 指向它即可,显式路径永远优先:
esCommand: 'C:\Program Files\MyImage\tools\es.exe'
es.exe 本身是 MIT 许可,所以自行分发是被允许的;许可声明随二进制放在 bin/LICENSE-ES.txt 里。
配置
设置 → 插件 → dsh-at-file-everything,配置卡片就在该 bundle 自己的页面上、描述下方。
控件 · 字段 · 默认 · 说明
es.exe 路径 · esCommand · 空 · 留空即自动探测;填入绝对路径可固定使用某一份客户端。
排除文件夹 · excludeInQuery · 空 · 文件夹名或通配符,在查询内剪掉。
排除文件 · excludeFilePatterns · 空 · 文件名或通配符,同样在查询内剪掉。
仅在结果里排除 · excludedDirectories · 空 · 只从返回行里过滤。查询时免费,但那些行已经消耗了配额。
为什么宿主半边也必须「认领」这个页面
只有浏览器卡片是不够的。插件页会先问宿主:这个命名空间还要不要自动生成页面。
const autoGenerate = presentations.get(fiber)?.auto ?? true
默认是 true。于是只在 plugins.bundle.config 注册了卡片的插件,拿到的是按 Config schema 自动生成的表单,自己的卡片根本不会被画出来。而全 volatile 的 schema 在那个通用表单里没有可渲染的常规控件——这就是「配置页看起来是空的」的成因。
所以宿主半边要显式认领,写法与所有自带页面的官方插件完全一致:
ctx.inject(['settings'], (scope) => {
scope.effect(() => scope.settings.configure({ auto: false }, ctx.fiber))
})
三点要记住:
- 只有卡片真的存在时才认领。 设了
auto: false却没有注册卡片,页面会是全空——比通用表单更糟。所以浏览器半边现在无条件向两个 slot 注册、每个都包try/catch,并且不再在configForms不可达时提前返回;卡片改为自己渲染「不可用」提示。 - slot 的 key 是包名,而设置命名空间是 Loader 的条目 id(这里是
file-reference-everything)。两个字符串都不能省:plugins.bundle.config按包名与页面清单比对,configForms.get()认的是条目 id。 ctx.locale.bind(ns)返回的是查表函数,不是文案对象。 它接受t('some.key');把返回值当{ label, hint }读,会让每个标题、说明、按钮文字都是undefined,而输入框(值来自数据)照常显示——于是一屏「空盒子」,看起来像布局坏了。所以本插件的文案是src/client.ts里的字面量表,只向 locale 服务问用哪种语言。test/render.test.js按 React 的方式展开组件,任何一处文案丢失都会失败——这才是能抓住它的检查。
怎么填
填名字或通配符,不是 Everything 的语法:
排除文件夹 [ Library ]
[ Lib* ]
[ Temp? ]
排除文件 [ *.meta ]
[ Thumbs.db ]
- 不用写前面的
!。 查询词由插件自己拼。粘贴!Library也接受,会把标记去掉——从
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::5,最后更新 2026-10-27