Skip to content

dsh-multi-root-explorer

Verified

dsh-multi-root-explorer · v0.1.6 · MIT · Web UI

Workspace Explorer for DeepSeek Harness: replaces the native sidebar workspace region with a multi-root browser — a conversation tree grouped by each session's real working directory, plus a read-only resource tree that previews files in the native right

Install

dsh plugin add dsh-multi-root-explorer

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Creators

Readme

dsh-multi-root-explorer(工作区资源管理器)

给 DeepSeek Harness 用的工作区浏览器:它接管左侧栏的工作区区域,换成两个选项卡——对话按每个会话真实的工作目录归入目录树,资源是一个只读的文件树,点文件用 DSH 原生右侧栏预览。

中文 | English

插件在侧边栏里的样子:已登记的工作区展开为带会话数量的文件夹,上方是「对话 / 资源」选项卡,以及小眼睛、刷新和设置按钮(目录名与会话标题已打码)

状态:预览版。 自动化测试全部通过,但尚未在所有 DSH 版本上做完整实机验收。安装前请先看已知限制。

它能做什么

  • 固定多根目录。 你导入的目录就是根,不会随着会话增减被重算成某个公共祖先。
  • 会话变成目录树。 每个会话按它真实的 cwd 归位:当前目录的直接会话排在前面,子文件夹排在后面,逐层递归。cwd 在根目录之外的历史会话保留独立入口;没有 cwd 的会话单独一组。
  • 资源只读浏览。 目录按层加载并分页,打开一个大盘不会先递归扫全盘。点文件交给 DSH 自己的右侧栏预览,主栏对话不受影响。
  • 两个选项卡各自独立的隐藏,且完全可逆。 点标题栏的小眼睛进入选择模式:已隐藏的项会显示出来并处于选中状态。点一个未选中的文件夹=选中它(整个子树随之显示为选中);点一个"被父目录覆盖"的子项=把它单独排除,祖先展开成同级项、其余保持选中;单击已选中的文件夹=它连同子树一起取消;双击已选中的文件夹=父目录取消、全部子项保持选中,提交后就是一个空文件夹。选中的后代按需向 Host 查询该目录自身的层级,所以没展开过的子文件夹同样会被覆盖。再点一次小眼睛就只隐藏最终选中的那些;隐藏只影响显示,不动任何文件、目录或会话记录。
  • 每个文件夹都有操作菜单。 右键或右端 ··· 可以在该目录里开启新会话、在系统资源管理器中打开;资源还支持复制绝对路径。
  • 拖进输入框就是原生引用。 资源行是标准拖拽源(text/plain、text/uri-list,外加一个插件私有的格式),效果固定为 copy。拖到对话输入框里时会自动提升成 DSH 原生的原子引用——文件图标 + 文件名 + 业务色,和从 @ 菜单里选中一个候选完全一样。提升失败时(例如落点紧邻文字、补全菜单没有为这段文本打开)会保留一条可读的引用文本并说明原因,不会把草稿弄乱。
  • @ 能检索所有工作区。 DSH 自带的文件补全只索引会话的工作目录,插件另外注册了一个 @ 源,覆盖全部已登记工作区(包括其他盘上的根),并在 Host 侧维护一份有界索引:沿用官方的排除目录策略(node_modules 等)、切片后台遍历、查询永不阻塞。
  • 工作状态诚实。 会话的状态卡和行首圆点不只看会话自己的 agent:子 agent 还在工作时(即使主 agent 已经停下)以及 goal 仍在逐轮推进时(两轮之间的空档),都显示「工作中」。
  • 支持原生归档。 归档/取消归档走 DSH 自己的服务,并且和「永久删除」明确分开。
  • 卸载后不会丢对话。 插件运行期间会把有普通会话的目录登记为 DSH 原生工作区,所以停用或卸载插件后,你的对话仍然能在原生界面找到。

环境要求

  • DeepSeek Harness >= 0.2.0-rc.1(声明在 dsh.engines.dsh)。
  • 浏览器需支持 Popover API(element.showPopover)。菜单和悬停卡渲染在顶层;不支持时插件会明确报错并关闭菜单,而不是把菜单放错位置。
  • 通过回环地址访问 DSH。插件的 HTTP 路由会拒绝非回环 socket、非回环 Host 头和跨站请求。

插件没有任何运行时依赖,只用 DSH 客户端模块加载器已经提供的 react。

安装

四种方式,任选一种。装完都要重启 DSH(重启应用与 Host),然后刷新页面 —— Host 路由不支持热重载,只刷新页面不够。

💡 方式 2、3 需要机器上有 dsh 命令。 桌面应用版用户如果提示找不到命令,请用 方式 1 或 方式 4 —— 这两条完全不依赖命令行。

1. 应用内插件市场(最省事,不依赖命令行)

打开设置 → 插件市场,搜索 dsh-multi-root-explorer,一键安装。市场会替你改好 profile 并提示何时重启。社区目录上架前(搜不到时)请用方式 2 或 4。

2. npm 包(命令行;永远取最新已发布版本)

dsh plugin --profile <你的 profile 名> add dsh-multi-root-explorer

写包名而不是 git 地址:包管理器会取 npm 上的最新已发布版本,以后发新版自动跟上。当前版本号见 npm 页面。

3. 从 GitHub 装(不经过 npm)

方式 A:预构建包(永远取最新发布版)

dsh plugin --profile <你的 profile 名> add https://github.com/Nethur-auro/dsh-multi-root-explorer/releases/latest/download/dsh-multi-root-explorer.tgz

链接里的 latest 在请求时解析,而资源文件名刻意不带版本号,所以它永远指向最新一次发布、也不会随发版失效。(本插件没有构建步骤:lib/ 是随仓库提交的成品,scripts 里只有 test,所以预构建包和源码装出来的东西一致。)

方式 B:源码(跟踪 main 分支)

dsh plugin --profile <你的 profile 名> add git+https://github.com/Nethur-auro/dsh-multi-root-explorer.git

装的是仓库 main 的当前提交。本项目每次发布都把 main 停在那个发布提交上,所以 main HEAD 与最新 tag 始终是同一点 —— 源码装到的就是最新发布版。代价是包管理器按 commit 缓存,升级要重装一次。

4. 手动改 profile(不依赖命令行)

插件管理器本质上就是改你 profile 的 $DSH_HOME/profiles/<你的 profile 名>/package.json(默认 ~/.dsh)。加依赖和 bundle 条目:

{
  "dependencies": {
    "dsh-multi-root-explorer": "^0.1.4"
  },
  "dsh": {
    "profile": {
      "bundles": ["dsh-multi-root-explorer"]
    }
  }
}

然后在该目录里 pnpm install,再重启 DSH。手动做和插件管理器做的是同一件事,所以除非有特别理由,优先用方式 1 或 2。

怎么确认装上了

看左侧栏:工作区区域应该出现对话 / Conversations和资源 / Resources两个选项卡,并且有插件自己的标题栏。如果看起来没变化,说明 Host 还没加载新代码——重启 DSH,而不是只刷新页面,Host 路由不支持热重载。

使用

  1. 点「添加工作区」选目录,需要几个根就加几个。
  2. 「对话」页显示文件夹和会话;「资源」页显示文件夹和文件。目录第一次展开时才加载该层。
  3. 点文件用右侧栏预览。预览需要一个可用会话作为授权上下文。
  4. 文件夹右键或 ···:在该目录开启新会话、在系统资源管理器中打开、(资源)复制路径。
  5. 会话右键:打开、重命名、归档、永久删除。永久删除有风险,见安全与永久删除。
  6. 用小眼睛按选项卡隐藏条目;被你隐藏的东西都能在设置里恢复显示。
  7. 「显示隐藏项」额外显示点开头的名字、node_modules 和 dsh-acl-recovery——它们默认是隐藏的。

数据与隐私

插件自己的数据都在 $DSH_HOME/storages/workspace-explorer/ 下:

  • config.json——版本 3:根目录、目录别名、两份互相独立的隐藏列表、展开状态和偏好。v1/v2 在迁移前会先备份;文件损坏或版本比程序新时会拒绝覆盖,而不是静默替换成空配置。
  • native-bridge.json——插件自己的幂等与进度记录。它只是记账,不是你的会话或原生工作区状态的副本。

写入是串行化 + 原子替换的。移除 bundle 不会删掉这个目录;要清理请先确认没有待完成的新会话操作,并且绝不要把删除 DSH 会话数据当成插件清理的一部分。

资源浏览从头到尾都是只读的:插件不会在你的工作区里创建、写入、移动或删除任何文件。请求受注册根、realpath 边界(含中间链接)和回环/同源校验限制。

安全与永久删除

永久删除是一个早就存在、被刻意隔离的功能,而且不可恢复。 目标 DSH 版本没有官方会话删除接口,所以那一个动作会直接移除会话自己的持久化产物。它依赖具体版本的存储结构,不是 DSH 支持的接口,应当只当作最后手段。请优先使用归档。

停用插件、卸载插件或清除配置,都不会触发这条代码路径。

已知限制

  • 自动化测试是契约测试,不是实机验收。 它们用 mock,不碰你的真实会话。「已命名但未发出」的会话跨新 Host 进程是否恢复、卸载后原生列表是否立刻可见、不同缩放下浮层位置是否正确,都还需要在你的版本上验证。不要在正在工作的 profile 上强制退出做持久化测试。
  • 把拖拽提升成引用用到了未公开的接口。 编辑器只把 text/plain 当纯文本插入,而引用块只能由输入管线在"选中一个补全候选"时生成,所以插件在文本落位之后调用管线自己的 pick 来补上这一步。每一环都做了能力探测,任何一环对不上就退化成引用文本(也就是拖拽最初的行为);但 DSH 升级若改了这些内部字段,自动提升会失效——lib/client.js 里标了 ADAPTATION POINT 注释,写明了依赖的确切字段,便于跟着适配。
  • 选中一个文件夹需要一次 Host 查询。 资源树是分页懒加载的,而"选中父文件夹"必须覆盖它的全部子项,所以插件在点击时按需读一次该目录自身的层级(并翻完分页),而不是只看已经展开过的部分。目录特别大时这一次点击会稍慢。
  • 菜单需要 Popover API。 浏览器不支持时,插件会给出明确错误提示,而不是显示一个位置错乱的菜单。
  • 改源码不等于改了运行中的 Host。 在本仓库改文件不会更新正在运行的 DSH;Host 路由需要重启。

测试

无依赖、无需安装:

node test/run.mjs
# 或 npm test

运行器会找出所有 *.test.mjs 和 selftest.mjs,用同一个 Node 可执行文件依次跑完(当前 158 + 138 + 15)。覆盖范围:目录列举与分页、配置与迁移与各项上限、路由族与信任栅栏、路径命名空间与跨平台拼写、对话树模型、隐藏作用域与目标边界校验、引用文本与 @ 候选索引及排序、拖拽提升的每一条降级路径、工作状态折叠(子 agent 与 goal)、原生桥接的创建/重命名/保存契约。

设计文档

许可

MIT © 2026 Nethur-auro