dsh-multi-folder
Verified@zfgcta/dsh-multi-folder · v0.4.2 · MIT · Web UI
DeepSeek Harness plugin: secondary working directories for a project. The agent keeps the primary workspace as cwd, gains equal write/exec permissions on configured secondary directories under workspace-write mode, and is notified of configuration changes
Install
dsh plugin add @zfgcta/dsh-multi-folder Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-multi-folder
English | 中文
给一个 DSH 项目配置若干副工作目录:Agent 的主工作目录不变,但对这些目录拥有同等读写与命令执行权限,还能在输入框用
@直接引用里面的文件。
效果
| 场景 | 表现 |
|---|---|
| 输入框左下角 「+」 | 「指令」分组里出现 「多工作目录」(草稿是否为空都在);点击打开宿主自带选择弹层(与「模型」选择器同一套 UI) |
| 弹层:添加 | 首行「添加工作目录」→ 原生目录选择器;远程 / 桌面壳等没有原生选择器可用时,自动改用插件自绘的目录浏览器(面包屑返回、逐级进入、可就地新建文件夹,走到盘根后可进「这台电脑」切换磁盘);重复添加同一目录只保留一条并给出提示,可连续添加多个 |
| 弹层:移除 | 每个已配置目录一行 → 点击进二次确认(勾选后方可「移除」,取消回到列表) |
输入 @ |
副目录里的文件以独立分组「多工作区目录」出现在候选中,选中插入其绝对路径,Agent 可直接 read;目录行可按 Tab 逐级下钻(与主工作区一致),面包屑可回退上一层 |
| 会话中 | 副目录清单注入系统提示词;配置变更在下一条消息或工具调用边界不打断地告知 Agent |
| 多窗口 / 手改配置 | 配置写入宿主自有存储,读取时按文件版本校验——另一个窗口或手工编辑的改动下一次读取即生效,无需重启 |
| 写文件 / 执行命令 | write / edit / pwsh / bash 命中副目录时自动以该目录为沙箱根执行,各模式语义与主工作区一致;read / glob / grep 本就不受限 |
效果展示
「+」菜单入口 —— 输入框自带菜单里的「多工作目录」一行:本地化名称、目录图标、一句话说明:

@ 引用 —— 输入 @ 时,已配置的副工作目录以独立分组列出:

命令式入口(等价能力,也供 Agent 使用):
/multi-folder list
/multi-folder add "D:\path\to\repo"
/multi-folder remove "D:\path\to\repo"
/multi-folder set "D:\a" "D:\b"
安装
dsh plugin --profile web add @zfgcta/dsh-multi-folder
GitHub 访问不稳定时,上面的 npm registry 方式正是省事的那条路;也可从 git 或本地 clone 安装(路径用正斜杠):
dsh plugin --profile web add git+https://github.com/HelloQingTao/dsh-multi-folder.git
dsh plugin --profile web add file:D:/projects/dsh-multi-folder
安装后需重启 DSH 后端(宿主插件在进程启动时装载)并刷新浏览器页面(客户端 bundle 即时提供)。卸载:dsh plugin --profile web remove @zfgcta/dsh-multi-folder。
要求
- Node.js >= 20
- DSH profile 由
@deepseek-ai/dsh-base+@deepseek-ai/dsh-web-app组成(标准 web profile) - 无构建步骤:宿主半边是纯 ESM,客户端是 DSH client-modules 格式的手写 factory bundle
DSH 兼容性
| DeepSeek Harness | 支持情况 |
|---|---|
| 0.1.7 及更新 | ✅ 完全支持:「+」菜单弹层、@ 引用副目录文件、跨窗口配置一致(已在 0.1.7-rc.2 实测) |
| 0.1.6 及更早 | ❌ 不支持 |
「+」菜单弹层与 @ 引用依赖 0.1.7 起随宿主提供的 commandUi / inputTriggers 客户端服务;在更早版本上,宿主不会提供这两个服务,本插件不会报错,但这两项功能无法使用。
本包与同名包 dsh-multi-folder 只能装一个:两者占用同一套运行时标识(/multi-folder 命令、multiFolder 服务与 multi-folder 词条命名空间、副目录配置目录),同时安装会导致其中一个装载失败。请先卸载另一个。
原理
- 权限换根:监听
tools/execute环绕瀑布,对目标路径(或workdir)落在副目录内的write/edit/pwsh/bash调用短路,改用换根后的会话站立策略执行({ ...standing, workspaceRoot: 副目录 })。模式不变,所以read-only仍拒绝、workspace-write放行。匹配前先经fs.resolve+processPath规范化,..、符号链接、大小写都能正确判定。后台任务(run_in_background)以同一策略注册进通用 jobs 运行时,job_output/job_kill照常可用。 - 单一可写根约束:Windows ACL runner 为每个进程树只授予一个可写根,因此留在主工作区的命令无法向副目录建文件(
git -C <副目录>、脚本内cd都不行,表现为 OS 级Permission denied)。创建文件的命令必须把workdir设为要写入的目录且用绝对路径;命中此模式时插件会在工具结果上附带一条修正提示。 - 「+」菜单入口:宿主端把
/multi-folder注册进人机命令表,官方「+」菜单的「指令」分组即由它驱动。两点关键:① 命令不声明input.hint——官方分组在非空草稿下会隐藏带提示的行,而这行必须常驻;② 客户端用ctx.commandUi.decorate给这条宿主命令挂popupSelect规格,于是点击打开的是宿主自有的选择弹层而非填入裸命令。列表/移除只提供数据(条目、文案、confirmation二次确认规格),不自绘弹层;唯一自绘的是「添加」用的目录浏览器(见下),因为宿主弹层是一次性选择器,无法承载逐级浏览。 @引用与下钻:经ctx.inputTriggers.registerSource注册一个@触发的独立来源(trigger 相同、name不同,故与宿主自带来源共存、各自成组),向宿主multiFolder/listFiles端点询问每个副目录的直接子项并以绝对路径呈现。目录行带drill: true,于是 Tab 逐级下钻(插入带尾斜杠的目录、空格路径保持引号开放,与宿主formatFileMention同形),并实现header面包屑回退上层。查询同时接受别名形态(@副目录名/余下路径)与下钻插入的绝对形态,故可连续下钻。该来源整体 try/catch 且限 30 条,失败只退化为空分组,按source-failed语义不影响宿主结果。- 自绘目录浏览器:
uiWorkspace.pickDirectory()只有桌面端能用——宿主组的是 browse 后端(局域网绑定、远程客户端、桌面壳)时直接回directory-picker/unavailable。因此「添加」在原生选择器不可用(或拒绝)时,降级为插件自绘的浏览器,数据走新增的multiFolder/browse(列子目录,骑fsseam,全部署通用,单层上限 1000 带truncated)与multiFolder/makeDir(校验单段名后mkdir)。两者都不碰配置存储:选定后仍经本模式的既有通道提交(会话内/multi-folder add,新会话页multiFolder/add)。样式全部取自--dsw-alias-*令牌,因此跟随主题与换肤。 - 配置与安全边界:per-workspace 配置为 JSON 数组,存于所有 Agent 沙箱之外的宿主目录
<DSH_HOME>/storages/multi-folder/<workspace-key>.json;对它的直接write/edit一律显式拒绝——Agent 无法自我授权,配置权只属于用户。缓存以fs.stat的版本保持一致:仅当文件仍是"当初读取时那个版本"才复用,否则回盘(版本在读之前取戳,避免把较旧内容标成较新版本);每次读取都做规范化去重。详见 SECURITY.md。 - 无会话远程 API:
multiFolder命名空间经ctx.typert.register以手写src-json描述符注册,并提供同名普通对象服务;list/add/remove/set/listFiles以工作区路径为键,与命令共享同一套校验核心,因此首个消息之前的新会话界面也能直接配置。listFiles带围栏:目标目录必须落在该工作区已配置的副目录之内,否则返回空——该端点不会退化成通用路径枚举器;browse/makeDir以路径为键,只服务浏览器本身。
已知问题
- 旧版本(≤0.3.0)在已停止的会话里点「+」,指令分组可能为空:升级到 0.4.0 即修复(根因是通知写日志时抛错、遗留了会话写句柄);临时办法是新开会话或重启 DSH。
开发与文档
- 测试:
node test/smoke-host.mjs(宿主 apply + remote API + 缓存一致性 +listFiles围栏 +browse/makeDir)、node test/intercept.mjs(拦截/命令/通知)、node test/activation.mjs(可选服务缺失时仍能激活)、node test/at-source.mjs(@查询解析、Tab 下钻、面包屑)、node test/browser.mjs(自绘浏览器全流程) - 架构与演进:docs/design.md;安全模型:SECURITY.md;变更:CHANGELOG.md