🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI
广告☁️ 云服务器特惠阿里云首购 8 折 · 腾讯云合作特惠
DeepSeek Harness Hub
← 返回列表

huuthuan-nguyen/dsh-tgrep

DeepSeek Harnessspec-screened扫描:中风险在 GitHub 查看 ↗
✓ 可直接安装

⚡ DSH-Tgrep:面向 DeepSeek Harness 的 Trigram 索引代码搜索

自动检查通过:npm 包已发布且 engines 声明满足基线(声明 Node >=22.19.0);该结论来自程序自动检查,未经人工实机验证。 · 最近上游提交 2026/9/22 · 已提供中文文档

⚡️ 由 Microsoft tgrep 驱动的 DeepSeek Harness 超快三元组索引代码搜索。

综合分
30.5
GitHub 分
30.5
用户评分
—
★ Stars
1
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add dsh-tgrep
npm 包 dsh-tgrep 已校验归属本仓库,走 npm 安装最省事
信任档位:已验证本站已于 2 天前真实安装成功(L4 · 真实安装)
是什么
dsh 原生插件 · tool
装得上吗
本站已真实安装成功(L4 · 真实安装,非静态推断)
安全吗
本站尚未对该插件做风险分级(暂未覆盖,不等同于无风险)
还在维护吗
活跃:最近一次提交在 4 天前

档位由下列信号合成:本站实装验证(真实安装,当前最高到 L4)· 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。下方各区块是它的证据明细。 验证判据与等级说明 →

🟢实装验证通过· 2026/9/23
由本站实装验证器在真实 dsh 环境安装成功,非静态推断。
数据截至 2026/9/24(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查✓ 自动检查通过

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-tgrep @ 0.1.12
✓Node 引擎要求 >=22.19.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/20 20:44:57

用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

由 DeepSeek 最新模型翻译生成
⚡ DSH-Tgrep:面向 DeepSeek Harness 的 Trigram 索引代码搜索

npm version
GitHub release
License: MIT
DeepSeek Harness plugin
topic: dsh-plugin
Powered by Microsoft tgrep

面向 DeepSeek Harness 智能体的 Trigram 索引 grep —— 在中大型代码仓库上实现快速代码搜索。
使用 Microsoft tgrep 遮蔽内置的 grep 工具,同时保留原生 Web GUI 搜索卡片、程序化工具调用(PTC)模式以及原版工具契约不变。

🌟 为什么选择 DSH-Tgrep?

原版 grep 工具自带一个捆绑的 ripgrep 二进制文件,并在每次调用时重新扫描工作区。
在中大型代码仓库上,相同的文件会被反复读取,相同的模式会被反复匹配,
因此搜索延迟会随着目录树的规模以及智能体搜索的频率而增长。dsh-tgrep 保留了模型已经熟知的
确切工具,只替换了底层的引擎:

1. Trigram 索引 —— tgrep 通过其 trigram 索引来回答查询,因此对已索引目录树的重复搜索
无需重新遍历每个文件即可返回(在中等规模仓库上实测约 15× —— 参见 ⚡ 性能与权衡)。
2. 自动守护进程 —— 工作区的 tgrep serve 会在首次使用时为你启动
(autoStartDaemon),并在多次调用之间保持热状态 —— 如果你通过
stopDaemonOnExit: false 让它保持温暖,跨会话也能保持。无需为每个项目手动执行 tgrep serve .,无需每次调用都冷启动,
并且默认情况下,当 harness 退出时不会留下孤儿进程。
3. 优雅降级 —— 在没有索引也没有守护进程的情况下,tgrep 会像今天的
grep 一样扫描文件,因此全新的工作区仍然可用。
4. 智能体平面遮蔽 —— 该工具注册到每个智能体自己的工具作用域中,因此它
按智能体替换内置的 grep,而无需触碰原版系统预设或宿主
注册表条目。
5. 契约对等 —— 原版的 pattern / path / include schema、30 秒协作式
超时、原生搜索卡片背后的 SearchMeta 负载,以及调用/结果
呈现器全部保留;case_insensitive 和 max_results 则在此基础上额外提供。
6. 安全的参数处理 —— 免疫 CLI 标志注入(--regexp 和 -- 边界)。
7. 零运行时依赖 —— 纯现代 ESM JavaScript,无构建步骤,也不导入
任何 harness 内部。

🚀 关键亮点与对比

| 特性 | 原生 grep(dsh-tool-fs-search) | dsh-tgrep |
|---|---|---|
| 搜索引擎 | 捆绑的 ripgrep 二进制文件 | Microsoft tgrep(三元组索引) |
| 在已索引仓库上的速度 | 每次调用 O(字节数) — 重新读取目录树 | ✅ 由索引提供服务(实测约 15×;见 ⚡ 性能) |
| 重复查询 | 每次调用都重新扫描目录树 | ✅ 已索引时由索引提供服务(回退到扫描) |
| 持久守护进程 | ❌ 无 | ✅ tgrep serve |
| 无索引时 | ✅ 全量扫描 | ✅ 优雅回退扫描 |
| 超过 64 MiB 的文件 | ✅ 会搜索(无大小上限) | ✅ 会搜索 — 默认无上限(maxFileSize 可选择启用上限) |
| 工具参数 | pattern、path、include | ✅ 相同 + case_insensitive、max_results |
| 协作超时 | ✅ 30 000 ms | ✅ 30 000 ms(对等) |
| Web GUI 搜索卡片 | ✅ 原生 | ✅ 原生(presentCall / presentResult + SearchMeta) |
| 会话日志元数据 | 上限为 64 KiB | ✅ 上限为 64 KiB,2000 字节的行预览 |
| 超出上限的结果 | 将完整列表溢出到工作区文件 | ⚠️ 内联截断提示(无溢出文件) |
| PTC 模式 | ✅ | ✅ |
| Agent 平面遮蔽 | 宿主注册表条目 | ✅ 按 Agent 遮蔽,预设不受影响 |
| 运行时依赖 | 捆绑的 ripgrep | ⚠️ PATH 上的 tgrep 二进制文件 |
| 构建步骤 | 随 harness 一起编译 | ✅ 无 — 纯 ESM |

⚡ 性能与权衡

grep 和 ripgrep 在每次搜索时都会扫描每个文件 — 每次查询 O(总字节数)。tgrep
预先构建三元组索引,因此搜索只会触及可能匹配的文件,而运行中的
tgrep serve 会让该索引保持热态。Microsoft 在
BENCHMARKS.md 中发布了完整结果 — 在大型仓库上比 ripgrep 快多达 52×,在 18 个测量单元中赢了 17 个(索引
预先构建,每次查询的平均延迟):

| 仓库 | 文件数 | 平台 | ripgrep | tgrep | 加速比 |
|---|---:|---|---:|---:|---:|
| gecko-dev | 388 K | macOS arm64 | 33,402 ms | 643 ms | 51.9× |
| linux | 96 K | macOS arm64 | 5,390 ms | 256 ms | 21.0× |
| chromium | 504 K | macOS arm64 | 41,806 ms | 2,643 ms | 15.8× |
| rust | 62 K | Windows | 1,489 ms | 194 ms | 7.7× |
| kubernetes | 31 K | Windows | 1,342 ms | 190 ms | 7.1× |

在本机本地测量(macOS arm64,deepseek-harness/packages,5,672 个文本文件,
160 MB,模式 defineTool,226 个匹配,3 次取最佳):

| 模式 | 延迟 |
|---|---:|
| 带索引的 tgrep | 15 ms |
| 带索引的 tgrep + -g '.ts'(即 include 发送的内容) | 15 ms |
| tgrep --no-index(grep/ripgrep 始终执行的暴力扫描) | 232 ms |

因此,在中等规模仓库上大约快 15×,而构建该索引只用了 0.7 s。请注意,
传递一个正向的 -g glob 过滤器——这正是该工具的 include 参数所做的——会保留索引带来的好处,而不是退回到扫描。

当优势缩小——甚至反转时

- 没有索引,就没有好处。 如果没有已构建的索引(或正在运行的 tgrep serve),tgrep 会像 ripgrep 一样进行扫描,因此在大目录树上的首次搜索并不会更快。autoStartDaemon(默认开启)免去了手动步骤:首次搜索时会为工作区根目录启动守护进程,后续搜索会复用它——参见索引与后台守护进程。
- 小型仓库和宽泛查询。 优势取决于仓库大小以及查询返回的匹配数量:一次返回数万条匹配的搜索,在交付这些匹配上花费的时间,比索引在查找它们上节省的还要多。在 Kubernetes/Linux 上,Microsoft 测得两者几乎持平(0.93×)。
- preferServer: false 在此插件的配置中会传递 --no-index,有意选择暴力扫描(当索引可能已过期时很有用)。
- 超过 64 MiB 的文件。 tgrep 默认会跳过它们,而 ripgrep 会搜索它们——这是一个有意的差异。此处已验证:一个 74 MiB 的文件在传入 --no-max-filesize 之前报告无匹配。由于静默丢弃匹配会破坏 grep 语义,此插件默认传递 --no-max-filesize。在以巨大生成文件为主的工作区中,这种取舍可能会增加扫描时间;如果你想要上限,可以重新启用:

config:
maxFileSize: "64M"   # 或 "8M",或字节数

- 更大的标志会退回到扫描。 放宽范围的标志(-E/--encoding、-a/--text、--binary、--no-ignore 系列)会绕过索引,因此使用它们的 extraArgs 会放弃加速——而单个指定文件总是被直接读取。

运行 tgrep status . 查看是否有服务器/索引正在为你的目录树提供服务,或运行 tgrep  . --stats 查看查询计划和计时。

🧩 工具契约

dsh-tgrep 在代理平面上遮蔽了 grep,因此模型看到的是这个契约,而不是原版 ripgrep 的契约:

| 字段 | 值 |
|---|---|
| parameters | pattern(必需)、path、include、case_insensitive、max_results |
| timeoutMs | 30000 —— 由 harness 工具调用超时策略通过 exec.signal 强制执行 |
| presentationMeta | SearchMeta(shape: 'matches'),带逐行预览,上限为 64 KiB |
| presentCall / presentResult | 通用搜索调用卡片 + 原生搜索结果卡片 |

case_insensitive 和 max_results 是对原版工具的扩展。与原版工具不同——原版工具会将超出上限的结果溢出到工作区文件——dsh-tgrep 会内联报告截断说明,并且从不写入恢复文件。

⚠️ tgrep 默认还会跳过大于 64 MiB 的文件,而 ripgrep 会搜索它们。此插件传递 --no-max-filesize,因此遮蔽 grep 不会静默丢弃匹配;改为设置 maxFileSize(例如 "64M")来启用上限——参见
⚡ 性能与权衡。

📋 前置条件

1. Node.js:>= 22.19.0
2. Microsoft tgrep:必须已安装并可在 PATH 中访问。
- macOS (Homebrew):
brew install tgrep

- Cargo(任何安装了 Rust 的平台):
cargo install tgrep

- 预构建二进制文件:从 Microsoft tgrep Releases 下载。

📦 安装与快速开始

你无需手动克隆或编译此仓库。DeepSeek Harness 会将其直接安装到任意 profile 中:

方法 1:从 NPM Registry 安装(推荐)

For the Web GUI profile
dsh plugin --profile web add dsh-tgrep

Or for a headless / TUI profile
dsh plugin --profile tui add dsh-tgrep

安装 registry 中的 latest 版本——可用 npm view dsh-tgrep version 查看。

方法 2:直接从 GitHub 安装

代码相同,无需 registry——适合固定到某个确切的发布版本:

Latest from the default branch
dsh plugin --profile web add github:huuthuan-nguyen/dsh-tgrep

Or pinned to a release tag
dsh plugin --profile web add github:huuthuan-nguyen/dsh-tgrep#v0.1.13

方法 3:从本地检出安装(供开发/贡献者使用)

dsh plugin --profile web add ./dsh-tgrep

注意: 安装后,只需重启你的 DeepSeek Harness profile(例如 dsh web 或 dsh --profile web)。

⚙️ 配置

安装后,dsh-tgrep 会提供一个默认配置层。你可以在 profile 的 cordis.patch.yml 或 $DSH_HOME/cordis.patch.yml 中自定义设置:

- insert:
- id: tgrep
name: dsh-tgrep
config:
Enable or disable the plugin
enabled: true
Prefer connecting to a running tgrep serve instance or local index
preferServer: true
Soft limit on returned matches (model context protection)
maxLines: 300
Extra CLI flags passed to tgrep (e.g. ["--hidden"])
extraArgs: []
Files larger than this are skipped by tgrep. Omitted (the default)
means --no-max-filesize, matching grep/ripgrep coverage; set a size
such as "64M" or "8M" to cap instead.
maxFileSize: "64M"
Start a tgrep serve daemon for the session workspace when none is
running, so no manual tgrep serve . is needed per project.
autoStartDaemon: true
How long a search waits for a freshly spawned daemon to bind before
proceeding anyway (it scans meanwhile, so the call never fails).
daemonReadyTimeoutMs: 5000
Stop the daemon this plugin started when the harness exits, so it does
not linger as an orphan. Set false to keep it warm across sessions.
stopDaemonOnExit: true
Stop an idle daemon after this many milliseconds without a search
(default 30 minutes; 0 disables). Every search resets the deadline.
daemonIdleTimeoutMs: 1800000
tgrep serve 守护进程的额外标志。上面的 extraArgs 属于
搜索,因此这是配置守护进程本身的唯一方式。
["--exclude", "data"]                        索引时跳过某个目录
["--watch-mode", "poll", "--poll-interval", "300"]  更省资源的刷新
["--no-watch"]                               只索引一次,永不刷新
daemonArgs: []

🗂️ 索引与后台守护进程

tgrep 无需索引即可开箱即用,通过扫描文件来工作——只是这样并不更快。
索引才是解锁上方 ⚡ 数字的关键。

通常你无需做任何事。 在 autoStartDaemon 开启(默认值)的情况下,工作区中的第一次
grep 会检查 /.tgrep/serve.json 中是否有存活的 tgrep serve,若未找到,
则以分离方式启动一个:

- 守护进程服务于会话工作区根目录,在后台构建索引,并通过文件监视器保持其最新。
搜索从不等待它:当它仍在索引时,搜索会像以前一样扫描目录树,因此第一次调用是正确的,
后续调用则很快。
- 其输出写入 /.tgrep/serve.log;就绪记录为
/.tgrep/serve.json({"pid":…,"port":…})。
- 启动守护进程会通告一次。 启动守护进程的那次搜索会在其结果中附带一条简短说明——
⚙️ tgrep daemon auto-started for this workspace (pid …)——并说明索引是否仍在构建中,
这意味着该次搜索不得不进行扫描。如果 git 可用且 .tgrep/ 尚未被忽略,该说明还会用
echo .tgrep/ >> .gitignore 提醒你,以保持 git status 干净。之后对同一守护进程的搜索
保持静默,而启动失败会说明其原因,而不是让你猜测。
dsh-knowcode
参考实现也通过将其自动启动说明追加到工具输出来做同样的事。
- 每个工作区一个守护进程。 并发调用共享单次进行中的尝试,而在竞争中落败的第二个
调用者会找到胜出者的记录,而不是再次启动。跨进程时,tgrep serve 本身会拒绝为同一
索引目录启动第二个服务器(“another tgrep server is already running for index directory …”),
因此两个 harness 在同一瞬间启动,最终仍然只会有一个服务器——已通过在一个项目上让两个
进程竞争并统计产生的 tgrep serve 进程数得到验证。与
dsh-knowcode 参考实现不同,此插件
不自带锁文件:tgrep 已经拥有该保护,再加一把锁只会是索引目录中多一个文件,而不会
增加任何安全性。
- 没有额外的索引产物。 该插件创建规范的 /.tgrep(tgrep 本来就会把
自己的索引放在那里),并恰好向其中添加一个文件:serve.log。没有其他
否则。索引从不会为一次搜索而构建:一次限定在 src/ 的搜索会通过固定的 --index-path 复用工作区索引,一次指向索引未覆盖的目录树的搜索会回退到扫描,而在一个无关项目中的搜索不会创建任何东西——所有这些都已验证。
- 被终止的守护进程留下的过期 serve.json 不会阻止重启:记录的 pid 会被探测,而不是被信任。tgrep 在每次退出时——包括手动 kill——都会留下它自己的 serve.json 和一个空的 serve.lock,而本插件有意不删除它们,因为它们是另一个工具的状态,而且无论如何重启都能正常工作。
- 搜索会传递 --index-path /.tgrep。tgrep 相对于搜索根目录解析其索引,因此如果没有这个参数,一次限定在 src/ 的搜索即使工作区服务器已启动也会进行扫描。当指向索引未覆盖的目录树时,tgrep 会回退到扫描,而不是给出错误答案。
- 当 harness 退出时守护进程会被停止。 它是以分离方式启动的,所以没有别的东西会结束它:插件的清理副作用——DSH 会在 SIGINT/SIGTERM 时运行它——会向它启动的守护进程发送 SIGTERM。你自己启动的服务器,或属于另一个仍在运行的 harness 的服务器,永远不会被触碰。索引保留在磁盘上,因此下一个会话的第一次搜索会在过期检查后复用它,而不是重新构建。
- 想让它跨会话保持热启动?stopDaemonOnExit: false 会让它继续运行。注意,此后插件永远不会停止它,即使在后来的退出时也不会——它不再是“它的”守护进程了。
- 空闲的守护进程会自行停止。 daemonIdleTimeoutMs(默认 1800000——30 分钟;0 禁用它)会在这么长时间没有通过插件进行搜索后结束守护进程,因此长时间的 harness 会话不会为每个访问过的项目累积一个服务器。每次搜索都会把截止时间往后推,仍在构建索引的守护进程绝不会在运行中途被停止,并且只有本插件启动的守护进程才有资格——你手动启动的服务器不会被触碰。tgrep serve 没有自己的空闲标志,所以截止时间存在于插件中;这也意味着它无法在 harness 消失后触发,而这正是上面 stopDaemonOnExit 所覆盖的情况。

tgrep 没有 stop 子命令,所以停止守护进程意味着向它的 pid 发送信号:

kill $(node -p "require('./.tgrep/serve.json').pid")

有两个值得了解的局限:被 SIGKILL 杀死的 harness(或崩溃)会跳过清理副作用并留下一个孤儿进程,下一个会话会收养并复用它,而不是杀掉它;而 0.1.6 之前版本留下的守护进程没有归属记录,因此必须手动停止一次。

想自己管理它?设置 autoStartDaemon: false——插件随后不会触碰任何守护进程和任何索引路径。手动操作仍然可用:

cd /path/to/your/project
tgrep index .        # 构建一次索引
tgrep serve          # 或保持一个服务器运行(自动构建并监视)
tgrep status .       # 显示此目录树是否有服务器正在运行

我需要运行 tgrep index 吗?

不需要。tgrep serve 会在没有索引时自行构建索引——它自己的日志就是这么说的:

[trace] no existing index found, will build in background
[trace] bootstrapping index with the external merge sort (memory-bounded)...
[trace] bootstrap complete: 1 files indexed in 0.0s (peak memory 12.0 MiB)

因为这个插件只会启动 serve——从不启动 index——所以那次构建始终使用守护进程自己的标志,因此 tgrep 警告的索引/serve 不匹配问题不会自行出现。

- 搜索从不构建索引:没有索引时它会扫描目录树,所以搜索绝不会在你背后创建 .tgrep。
- tgrep index . 仍然有其刻意的用途——在会话前预热大型目录树,或在更改 daemonArgs 中的成员标志(--exclude、--no-ignore、--max-filesize)后重建。用相同的标志重建——tgrep index . --exclude data,或 rm -rf .tgrep——因为服务器会把一个它看不到的已索引文件视为已删除。
- 在首次构建运行期间,搜索会进行扫描而不是返回部分结果,并且 tgrep status . 会将进度报告为 Indexing: …。

🔍 守护进程会触碰什么——以及如何验证

一个常见的担忧是,用一个带索引的守护进程来遮蔽 grep,意味着你的数据库、构建产物和其他大型二进制文件会在每次搜索时被读取。事实并非如此。在文件被读取之前,有三道独立的防线会将其拦截:

| 防线 | 规则 | 如何在你的项目上检查 |
|---|---|---|
| 忽略规则 | .gitignore(以及 .git/info/exclude、.ignore、p4ignore.ini)——仅在 git 仓库内应用,与 ripgrep 的做法一致 | tgrep --files . \| grep -i '\.db' → 无输出 |
| 二进制扩展名 | 约 65 种二进制扩展名会在遍历过程中被拒绝,这是 ripgrep 不会做的 | tgrep count-files . → … (N binary skipped …) |
| NUL 字节检查 | 会检查前 8 KB;出现 NUL 字节即判定该文件为二进制 | 一个真实的 SQLite 文件在没有任何忽略规则时会报告搜索了 0 files |

在一个真实的 103 MiB SQLite 数据库上实测,该数据库被复制到一个完全没有 .gitignore 的目录中:

Brute-force search completed in 3.1ms (0 files): 0 matches

0 files 意味着遍历在读取任何一个字节之前就拒绝了它——而且对该目录的搜索耗时 12 ms,与一个只包含一个小文本文件的目录完全相同(那 12 ms 是 tgrep 进程启动时间,而非 I/O)。强制使用 -a/--text 是使其内容可被搜索的唯一方法。

其他一切也都是有界的,而且现在可以通过 daemonArgs 进行配置(守护进程由插件启动,所以这是向它传递标志的唯一方式):
yaml
config:
daemonArgs: ["--exclude", "data"]                      # 永不索引此目录
daemonArgs: ["--watch-mode", "poll", "--poll-interval", "300"]   # 低成本刷新
daemonArgs: ["--no-watch"]                           # 只索引一次,不刷新
daemonArgs: ["--max-cpu", "25", "--max-memory", "2048"]  # 限制构建资源

当仓库在被忽略的路径中有大量变动时,可以求助于这些选项:在 macOS 上,tgrep 为整个根目录维护一个递归的 FSEvents 监视器,并在事件送达之后*过滤被忽略的事件,因此一个每秒重写数据库的进程仍然会让监视器付出逐事件的开销。基于轮询或禁用的监视器可以消除这一开销。

保持成员标志一致:tgrep 在启动时会将索引与文件系统进行比较,因此一个在构建时未使用 --exclude data、却在服务时使用它的索引会将这些文件视为已删除。更改 daemonArgs 后,请使用相同的标志重新构建——rm -rf .tgrep 或 tgrep index . --exclude data。不要在此处传入 --index-path:该插件会固定使用工作区自己的索引进行搜索。

检查项目的守护进程
bash
tgrep count-files .            # 遍历认为可搜索的内容
tgrep --files . | head         # 它将要搜索的确切文件列表
tgrep status .                 # 索引大小、服务器 pid/端口、索引进度
tail -n 20 .tgrep/serve.log    # 每次搜索的耗时:候选数、匹配数、耗时

一个健康的项目的 serve.log 是安静的——只有少量 search: 行和偶尔的 stale check,没有 overflow、fallback 或 error 行。

✅ 验证

要验证插件是否处于活动状态:
1. 启动 DeepSeek Harness:dsh web
2. 在聊天中,向模型提问:Search for "name" in package.json using grep.
3. 模型将调用由 tgrep 驱动的 grep,匹配结果将渲染在原生 Search 卡片中。

🧹 卸载

要从你的配置文件中移除 dsh-tgrep:
bash
dsh plugin --profile web remove dsh-tgrep

🧯 兼容性与故障排除

Cannot read properties of undefined (reading 'prepare')

这是 DeepSeek Harness 的缺陷,而不是 dsh-tgrep 的缺陷。 它已在零插件安装的配置文件上得到验证:每一次工具调用(bash、read、grep……)都会中止该轮次。

根本原因在于 harness:

- packages/core/agent-loop/src/tool-calls.ts 通过 ctx.tools[TOOL_RUNTIME_SCHEDULER].prepare(call.exec) 访问工具注册表,并且从不检查结果。
- TOOL_RUNTIME_SCHEDULER 是用 Symbol(...)(packages/core/tools/src/index.ts)声明的,而不是 Symbol.for(...),因此该键对某一个模块实例是私有的。
- Harness v0.1.6-alpha.2 将默认的 resolutionMode 从 link 改为 runtime(apps/cli/src/profile-boot.ts)。当 @deepseek-ai/dsh-tools 可通过两条解析路径到达时(工作区副本和配置文件安装锚点的符号链接),Node 会对其求值两次,两个符号不同,查找结果为 undefined,于是那句晦涩的 Cannot read properties of undefined (reading 'prepare') 会中止每一次工具调用。

在 harness 发布修复之前的变通方法:
bash
1. 一行本地 harness 补丁,然后重新构建宿主库:
packages/core/tools/src/index.ts
- export const TOOL_RUNTIME_SCHEDULER: unique symbol = Symbol('@deepseek-ai/dsh-tools.scheduler')
+ export const TOOL_RUNTIME_SCHEDULER: unique symbol = Symbol.for('@deepseek-ai/dsh-tools.scheduler')
pnpm run build:lib:host

2. 或者固定一个未更改默认值的 harness 版本:
dsh-v0.1.6-alpha.1

3. 或者强制使用之前的解析模式,如果你的 CLI 支持的话:
dsh --help | grep -i resolution

skipping …: No such file or directory 和 Last reconcile error

tgrep status . 可能会报告:

Last reconcile error: reconciliation incomplete; see logs for filesystem or publication errors; will retry

而 serve.log 会指出一个已经消失的文件:

tgrep: skipping /path/to/data/bot.db-shm: No such file or directory (os error 2)

这是一个良性竞态,而非过期索引。SQLite 创建和删除其 -wal/-shm
伴随文件——编辑器则创建和删除其交换文件——的速度比监视器查看它们的速度更快,而且在
macOS 上 tgrep 为整个根目录保留一个递归监视器,因此被忽略路径的事件仍会被传递并在之后被过滤。与其相信那一行日志,不如确认索引是健康的:tgrep status . 应显示 Indexing: complete、Reconcile: idle,以及最近的
Last successful reconcile,而 tgrep --files . | grep -i '\.db' 不返回任何内容。

要减少这种频繁变动,可以阻止守护进程对每个文件系统事件都做出反应:
yaml
config:
daemonArgs: ["--watch-mode", "poll", "--poll-interval", "300"]   # 每 5 分钟进行一次元数据扫描
daemonArgs: ["--no-watch"]                                     # 只构建一次,从不自动刷新

对于这样的目录,在 daemonArgs 中设置 --exclude data 也值得一试:它能将这些文件
完全排除在索引遍历之外。之后用相同的标志重建索引。

为什么 dsh-tgrep 不导入 harness 内部实现

自 0.1.2 起,该插件在设计上就是零依赖的:它从不导入
@deepseek-ai/dsh-tools(或任何其他 harness 包),并将其工具契约声明为
纯 JSON Schema。第三方插件不应在运行时加载 harness 内部实现——这样做
可能会再添加一份被求值的包副本,并且在使用私有 symbol 键时,破坏
宿主自身的查找。该插件与 harness 的契约正是传递给
tools.register() 的那个对象。

许可证

MIT © Thuan Nguyen

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

同作者(huuthuan-nguyen)的其他插件

💬 加入社群

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

DPharness QQ 群二维码,QQ 扫码进群
QQ 扫码进群
DPharness 飞书群二维码,飞书扫码进群
飞书扫码进群