dsh-prompt-switcher
DeepSeek Harness (DSH) plugin: pick a local .md prompt template with / when starting a new conversation; it binds the whole conversation with AGENTS.md-level authority.
- Stars
- 0
- Language
- JavaScript
- Created
- Sep 24, 2026
- Updated
- Sep 30, 2026
Introduction
dsh-prompt-switcher
DeepSeek Harness(DSH) Web 插件。新建对话时输入 /,可以从本地目录里的 .md 提示词模板中选一个。模板的约束力等同于 AGENTS.md,对这个对话之后的每一轮都有效。还可以设置对所有新对话生效的全局提示词,用 {{env:变量名}} 在提示词中引用环境变量,并通过 WebDAV 在多台设备之间同步提示词模板和环境变量。还可以开启删除对话,在左侧对话栏的菜单中彻底删除对话。
- 兼容版本:DSH
>=0.1.7-alpha.1,即 0.1.7 系列的 alpha 版及之后的正式版;Node>=22.19 - 插件形式:标准 DSH bundle。
package.json里声明了dsh.bundle.patch和dsh.client,Host 半和 Browser 半都是纯 ESM,没有运行时依赖,安装时不需要构建 - 收录在 mikulo/dsh-plugins 插件清单中
功能
| 设置页 | 设置 → 提示词模板:选择模板目录(弹出系统文件夹对话框,也可以直接填路径),读取目录第一层的全部 .md 文件(不读子目录),每个模板有激活开关和「编辑」按钮,另有「刷新」按钮 |
/ 菜单 | 已激活的模板按文件名显示在 / 菜单里,默认排在 Harness 自带指令之前(可以关闭) |
| 约束整个对话 | 选中模板并发送第一条消息后,模板以 AGENTS.md 同样的方式写入会话,之后每一轮都生效。上下文压缩、恢复会话、分叉会话后都会保留 |
| 只对新对话生效 | 已经进行过对话的会话里使用模板会被拒绝:模板不生效,消息也不发出,草稿保留 |
| 全局提示词 | 选一个 .md 文件(以文件名作为名称),或者直接输入一段文本并为其取名(保存为模板目录下的 名称.md),作为全局提示词。开启后,之后新建的每个对话都会遵守它;在新对话里再用 / 选择模板时,模板追加在全局提示词之后,两者同时生效,不会互相覆盖 |
| WebDAV 云同步 | 独立的「WebDAV 云同步」标签页:配置服务器、可选的 HTTP / SOCKS5 代理、一键测试连接;「同步到本地」「同步到云端」列出对方目录第一层的 .md 文件,勾选后同步,遇到同名文件逐个询问是否覆盖(可全部覆盖 / 全部跳过) |
| 环境变量 | 独立的「环境变量」标签页:每行一个变量(变量名 + 字符串),可新增、修改、删除。提示词模板和全局提示词里的 {{env:变量名}} 在发送给模型前替换为对应的值,未定义的变量直接删除 |
| 环境变量同步 | 「WebDAV 云同步」里的「同步环境变量文件」开关:环境变量文件放在 WebDAV 目录根目录,支持「合并配置文件」「本地覆盖云端」「云端覆盖本地」;合并时同名同值合为一条,同名不同值逐个选择用云端还是本地的值 |
| 删除对话 | 独立的「允许删除对话」标签页(默认关闭):开启后,左侧对话栏每个对话的「…」菜单在「置顶 / 重命名 / 分叉 / 归档」之后多出红色的「删除对话」,确认后彻底删除该对话,不可撤销;「工作区」右侧的「视图选项」菜单多出红色的「删除所有已归档」,确认后删除全部已归档对话 |
| 模板编辑 | 整页编辑视图:大号等宽编辑框,Ctrl+S 保存,显示未保存状态,检测保存冲突,并自动同步在外部编辑器里所做的修改。「用其他程序打开…」会弹出系统的“打开方式”对话框,由你自己选择编辑器 |
安装
通过 dsh-plugins 清单(推荐)
git clone https://github.com/mikulo/dsh-plugins.git
cd dsh-plugins
node install.mjs --profile web --only dsh-prompt-switcher
直接安装
dsh plugin --profile web add github:mikulo/dsh-prompt-switcher
安装后重启 dsh web 并刷新页面。
- 更新:
dsh plugin --profile web update @mikulo/dsh-prompt-switcher,然后重启dsh web - 卸载:
dsh plugin --profile web remove @mikulo/dsh-prompt-switcher
升级 DSH 前后
DSH 仍是测试版,可能有破坏性更新。本插件和 DSH 本身都做了隔离,插件出问题时一般只是功能失效,不会导致 DSH 起不来:
- DSH 加载 profile 时,读不了或不兼容的插件包会被跳过;插件代码加载或初始化失败只记日志,其他插件照常运行。
- 本插件每个功能单独安装、单独容错:某个 DSH 接口变了,只有对应的功能不出现(例如「删除对话」菜单行),设置页和其余功能不受影响;每一步对话前运行的钩子在出错时原样放行,不会卡住对话。
- 删除相关的操作只在你点击时执行,并且删除前会先检查目录,不对就停止。
万一升级后 dsh web 异常,可以:
- 卸载插件:
dsh plugin --profile web remove @mikulo/dsh-prompt-switcher,再启动dsh web。设置和环境变量文件保存在~/.dsh/,不会丢失。 - 或者用不含第三方插件的 profile 启动做对比,例如
dsh --profile rescue(profile 里只列@deepseek-ai/dsh-base和@deepseek-ai/dsh-web-app两个 bundle)。
插件的 Browser 半会随页面热更新,Host 半必须重启
dsh web才会加载新代码。两边版本不一致时,设置页顶部会提示“请重启 dsh web”。
使用
- 打开 设置 → 提示词模板,点击「选择文件夹」,选中存放模板的目录。
- 只读取该目录第一层的
*.md文件,扩展名不区分大小写。列表按文件名显示。 - 新目录中的模板默认全部关闭,打开开关才会激活。
- 目录里的文件增删改之后,点击「刷新」。
- 只读取该目录第一层的
- 新建对话,在输入框输入
/,已激活的模板(例如代码审查)会出现在菜单里。继续输入文字可以过滤。 - 选中模板(或直接输入
/代码审查再按空格)后,输入框为/代码审查,接着输入第一条消息并发送。发送时插件识别开头的模板名并绑定模板,之后这个对话的每一轮都会遵守该模板。/代码审查以普通文字显示(不再变成蓝色命令词)。这是为了绕开 DSH 输入框的一个问题:命令词高亮会打断中文输入法的组字,导致拼音重复、文字变蓝且无法删除。- 中文输入法状态下按
/键会输入、。输入框开头的、(或全角/)会自动换成/,同样弹出模板菜单,两者等效。句子中间的、(如“苹果、香蕉”)不受影响。
仓库里的 examples/ 有两个示例模板,可以把它设为模板目录来体验。
全局提示词
在 设置 → 提示词模板 → 全局提示词 中配置:
- 选择来源:
- 模板文件:从模板目录的
.md文件中选一个,文件名(不含扩展名)就是全局提示词的名称。之后更换模板目录不影响已选的文件。 - 自定义文本:填写名称(必填)和内容,点击「保存」或按 Ctrl+S。内容保存为模板目录下的
名称.md(需要先配置模板目录),之后也会出现在模板列表里,可以用「编辑」修改。目录中已有同名的其他文件时,会先询问是否覆盖。改名后保存会写入新文件,旧文件保留。
- 模板文件:从模板目录的
- 打开「启用全局提示词」开关。配置不完整时开关打不开,并提示原因。
规则:
- 只对开启之后新建的顶层对话生效;已开始的对话和子代理会话不会被注入。分叉出来的会话沿用原会话的状态(原会话有就有,没有就没有)。
- 对话开始时绑定的是当时的快照:之后修改或关闭全局提示词,只影响再之后新建的对话。上下文压缩后会重新注入同一份快照。
- 在新对话中用
/选择模板时,会话里依次是 全局提示词 → 模板 → 你的第一条消息。模板追加在全局提示词之后,两者同时生效。/菜单中的模板说明会显示“追加在全局提示词「…」之后”。 - 全局提示词启用了但读不到(文件被删、内容为空等)时,设置页显示原因,新对话照常进行,只是不注入。
WebDAV 云同步
在 设置 → 提示词模板 顶部切换到「WebDAV 云同步」标签页:
- 服务器:填写存放模板的 WebDAV 目录地址(例如
https://dav.jianguoyun.com/dav/prompts/)、用户名和密码(坚果云等服务请使用应用专用密码)。密码只保存在本机,不会回传到页面;再次保存时密码框留空表示保持不变。 - 代理(默认关闭):打开「通过代理连接」后,可选 HTTP 或 SOCKS5,地址默认
127.0.0.1:7891,可以修改。开关决定「测试连接」和同步是否经过代理。暂不支持需要认证的代理。 - 测试连接:用表单里当前(可以尚未保存)的配置访问该目录,显示成功、认证失败、目录不存在、代理连不上等结果。
- 点「保存配置」后才能同步。
同步(只处理两边目录第一层的 .md 文件,不含子目录):
- 同步到本地:列出云端目录的
.md文件,默认全部勾选,「全选 / 取消全选」一键切换。点「同步到本地」下载到模板目录。 - 同步到云端:列出本地模板目录的
.md文件,操作同上,上传到云端目录;云端目录不存在时自动创建。 - 目标位置已有同名文件时,逐个询问「覆盖 / 跳过」,同名文件不止一个时还可以「全部覆盖 / 全部跳过」,也可以「取消同步」。完成后列出每个文件的结果(新增 / 覆盖 / 跳过 / 失败)。
说明:只支持 Basic 认证;单个文件上限 1 MiB;配置保存在 $DSH_HOME/dsh-prompt-switcher.json,其中密码是明文。
同步环境变量文件
打开「同步」下方的「同步环境变量文件」开关(默认关闭)后,点「同步环境变量…」。插件同时读取本地和云端的 dsh-prompt-switcher.env.json(云端位于 WebDAV 目录根目录),显示两边各有多少变量、哪些相同、哪些只在一边、哪些同名但值不同,然后选择同步方式:
| 情况 | 可选方式 |
|---|---|
| 两边都有文件 | 合并配置文件:两边的变量合并成一份,同时写入本地和云端。同名且值相同的合并为一条;同名但值不同的逐个列出云端和本地的值,由你选择(也可以「全部使用本地 / 全部使用云端」) 本地覆盖云端 / 云端覆盖本地:会被覆盖的一方如果有独有的变量,执行前会列出并确认 |
| 只有本地有 | 上传到云端(云端目录不存在时自动创建) |
| 只有云端有 | 下载到本地 |
| 两边完全一致 | 提示无需同步 |
同步时 Host 会重新读取两边的文件;比较之后如果文件又有变化,出现新的冲突时会重新让你选择。
环境变量
在 设置 → 提示词模板 顶部切换到「环境变量」标签页:
- 每行一个变量:左边是变量名,右边是对应的字符串。「新增变量」加一行,「删除」删掉一行,改完点「保存」(或 Ctrl+S)。「复制引用」把
{{env:变量名}}复制到剪贴板。 - 变量名以字母、汉字或
_开头,只能包含字母、汉字、数字、_、.、-,不能重复(区分大小写)。变量名和值都为空的行保存时忽略。 - 在提示词模板或全局提示词里写
{{env:变量名}}(花括号内允许空格,如{{ env: github_api }})。例如变量github_api=123456,模板里的github的api是{{env:github_api}}发送给模型时是github的api是123456;如果没有定义这个变量,就变成github的api是。 - 只识别
{{env:…}}这一种写法。{{name}}、<name>、${name}等其他写法原样保留,不会被误删。 - 替换发生在新对话绑定提示词的那一刻,写入会话的是替换后的快照。之后修改变量只影响再之后新建的对话。注意:变量值会以明文出现在发送给模型的内容和会话记录里。
- 页面打开后,如果文件被其他程序或云同步改过,保存时会先询问是否覆盖。未保存的修改在切换标签页或关闭设置对话框后保留到页面刷新前。
环境变量保存在 $DSH_HOME/dsh-prompt-switcher.env.json(默认 ~/.dsh/dsh-prompt-switcher.env.json),格式如下,也可以手动编辑(手写成 { "变量名": "值" } 这样的简单对象同样能识别):
{
"version": 1,
"variables": [
{ "name": "github_api", "value": "123456" }
]
}
这个文件不在模板目录里,卸载或重装插件都不会丢失。
删除对话
DSH 自带的对话菜单只能归档,不能删除。本插件可以加上删除功能:
- 打开 设置 → 提示词模板,切换到「允许删除对话」标签页,打开「是否允许删除对话」开关(默认关闭)。
- 在左侧对话栏把鼠标移到对话标题上,点击出现的「…」图标。菜单在「置顶对话 / 重命名 / 分叉对话 / 归档对话」之后多出红色的「删除对话」。
- 点击「删除对话」后弹出确认框「是否删除对话,删除不可撤销」,点「是」才会删除,点「否」取消。
删除时插件会:
- 先按「归档对话」的官方流程停止该对话正在运行的工作(当前轮次、后台任务、子代理、定时任务)。如果你正在查看这个对话,页面会自动离开它。
- 卸载该对话在当前
dsh web进程中已加载的实例,然后删除它在~/.dsh/sessions/<项目>/<会话 ID>/下的会话记录,连同它的子代理会话一起删除,并清理它的投影缓存、置顶和归档记录。 - 从这个对话分叉出来的对话是独立对话,不会被删除。
删除所有已归档
开关打开后,点击左侧对话栏「工作区」文字右侧的「视图选项」按钮,筛选菜单「隐藏已归档 / 全部对话(显示已归档)/ 仅显示已归档」下面多出红色的「删除所有已归档」。点击后弹出确认框「是否删除所有已归档的对话,删除不可撤销」,并显示将删除的对话数量;点「是」后逐个删除全部已归档的对话(每个都按上面的流程,连同子代理会话),某个失败不影响其余的,失败项会列在确认框里。指向已不存在会话的归档记录会顺便清掉。
这个菜单在 DSH 里是写死的列表,没有提供插件槽位,所以插件在菜单弹出时向它的页面元素里追加这一行(通过菜单的 …_viewOptionsMenu 样式类或「隐藏已归档」这一行识别,外观复用原有菜单行的样式)。DSH 以后改了这个菜单的结构时,这一行可能不再出现,但不会影响菜单原有的功能。
说明:
- 删除不可撤销。插件在删除前会校验目录确实是该会话自己的目录,校验不通过就什么都不做。
- 关闭开关后菜单中不再显示「删除对话」,Host 也会拒绝删除请求。
- 极少数情况下,已加载的对话实例无法卸载:会话记录照样删除,对话会暂时归档隐藏,重启
dsh web后完全消失。 - 开关保存在
~/.dsh/dsh-prompt-switcher.json的allowDeleteSession字段,重装插件不会丢失。 - 自定义目录:会话文件的位置由 DSH 自己的会话存储给出(
sessionPersistence.locate()),所以用DSH_HOME等方式改了数据目录,或在配置里改了会话存储根目录,删除都会作用到实际的位置;DSH 程序本身装在哪个目录与此无关。插件的设置文件同样遵循DSH_HOME(支持~/…写法)。 - DSH 本身没有删除会话的接口,这个功能依赖当前 DSH 版本(0.2.0-rc.1)的会话存储结构(JSONL)和内部结构。DSH 升级后如果结构变了,删除会报错并停止,不会误删其他文件。
编辑模板
在列表中点「编辑」进入编辑视图:
- 「保存」或 Ctrl+S 保存,「放弃修改」回到上次保存的内容,标题旁显示“已保存 / 未保存”,Tab 键插入两个空格。
- 用其他程序打开…:Windows 上弹出“你要如何打开这个文件?”对话框,macOS 上弹出“选取应用程序”对话框,Linux 上用默认程序打开。在外部编辑器保存后,编辑视图约 2 秒内自动同步。如果这里也有未保存的修改,会让你选择「载入磁盘版本」或「保留我的修改」。
- 文件在打开后被其他程序改过时,保存会被拒绝,并提供「载入磁盘版本 / 仍然覆盖保存」。
- 关闭设置对话框时,未保存的草稿会保留到页面刷新前。
- 保存时保留文件原有的 BOM 和 CRLF 换行。
- 修改模板只影响之后新建的对话。已经开始的对话使用的是第一次发送时的模板快照。
工作原理
| 需求 | 实现 |
|---|---|
| 设置页面 | Browser 半向 settings.section 槽位注册页面 |
| 文件夹对话框 | 优先使用 Host 的 directoryPicker 服务(原生对话框)。该服务不可用时,Windows 上用 PowerShell 的 FolderBrowserDialog,其他平台提示手动输入 |
| 设置与模板读写 | Host 路由 /api/dsh-prompt-switcher/*,只接受本机回环地址的请求。只接受当前目录扫描结果里的文件名,访问不到目录以外的路径。配置保存在 $DSH_HOME/dsh-prompt-switcher.json(默认 ~/.dsh) |
/ 菜单显示中文名 | Host 命令名只能用 ASCII,所以 Browser 半注册了自己的 / 输入触发源。选中后只插入普通文本 /模板名 ,按 Enter 时由触发源的 matchEnter 认领草稿,提交 Host 命令 /prompt-template <模板id> <消息>。不使用 onPick / matchSpace 认领,因为 DSH 的认领高亮会在输入法组字期间拆分文本节点。置顶就是调整触发源的 order |
、 等效于 / | DSH 的触发检测只认 ASCII /(TriggerChar 只有 / 和 @),所以 Browser 半在 document 上(捕获阶段)监听输入框 [data-composer-input] 的事件:直接提交的 、 在 beforeinput 中拦截并改插 /;输入法组字提交的 、 在 compositionend 之后选中并替换为 /。只处理输入框开头(前面只有空白)的位置,不修改 DSH 本体文件 |
| 绑定模板 | Handler 先确认这是新对话,然后 agent.inject(模板消息),再 agent.steer(用户消息),与官方 /plan 的做法相同 |
| 约束力等同于 AGENTS.md | 模板以带来源 {kind:'prompt-switcher', form:'instructions'} 的 <system-reminder> user 消息写入会话日志,措辞与 dsh-agent-instructions 一致,即不高于 system、developer 或用户的直接指令 |
| 持续生效 | 会话投影从完整日志中折叠出已绑定的模板快照,所以恢复和分叉会话都能还原。agent/pre-step 钩子在模板被上下文压缩掉后,重新注入同一份快照 |
| 全局提示词 | agent/pre-step 钩子在新顶层对话的第一轮,把全局提示词以来源 {kind:'prompt-switcher-global', form:'instructions'} 的 <system-reminder> 消息放在本步消息最前面;通过 / 模板开始的对话,由命令 handler 依次注入 全局提示词 → 模板 → 用户消息。同一会话投影同时折叠全局提示词和模板快照,压缩后按同样顺序补回 |
| WebDAV 同步 | Host 半只用 Node 内置模块实现 WebDAV 客户端(PROPFIND 列目录、GET 下载、PUT 上传、MKCOL 建目录,Basic 认证)。代理:HTTP 代理用 CONNECT 隧道,SOCKS5 由代理解析域名,隧道建立后再按需做 TLS。只接受当前列表里的文件名;是否覆盖同名文件由 Host 在写入前重新检查 |
| 删除对话 | Browser 半向官方槽位 sidebar.workspaces.session.menu.item 注册菜单行(order 500,排在归档之后,danger 红色样式),确认框注册在 shell.overlay(菜单关闭时会卸载,确认框不能放在菜单里)。Host 路由 POST /api/dsh-prompt-switcher/session-delete 依次:用 workspaceRegistry.archiveSession(id, { stopActivity: true }) 停止工作,通过 Agent 生命周期 effect(agentLoop.lifecycle(<id>))卸载已加载的实例,按 sessionPersistence.locate() 找到并校验会话目录后删除(含 origin: 'subagent' 的子会话),删除投影缓存行,清理置顶 / 归档记录,最后发出 api-session/removed 让所有页面移除该行 |
| 环境变量 | Host 在绑定模板 / 全局提示词时读取 $DSH_HOME/dsh-prompt-switcher.env.json,把 {{env:NAME}} 替换为对应的值(未定义则替换为空),再做 </system-reminder> 转义,所以变量值也无法闭合插件的框架。环境变量同步用同一个 WebDAV 客户端 GET / PUT 根目录下的同名文件,合并时同名不同值没有选择会返回 409 和冲突列表 |
注意:
- 子代理(subagent)的会话不继承模板,也不注入全局提示词。
- 单个模板上限为 1 MiB。模板中的
</system-reminder>会被转义。 - 设置接口只接受本机访问。通过局域网打开的 Web 页面不能修改本插件的设置。
开发
npm run build # src/ → lib/:写入版本号,校验 Host/Client 协议号与模块 id,并做语法检查
npm test # 确认 lib/ 与 src/ 一致,然后用伪造的 Harness 服务跑 Host 与 Client 冒烟测试,
# 并用本地假 WebDAV 服务器、HTTP 代理和 SOCKS5 代理跑同步测试
- 修改
src/后运行npm run build,并把lib/一起提交。插件通过github:安装时直接使用仓库里的lib/,没有安装时构建脚本。 - 修改 Host 与 Client 之间的接口时,同时递增
src/index.js和src/client.js中的HOST_PROTOCOL。
本地联调可以用 link 安装:dsh plugin --profile web add link:<本仓库绝对路径>。改代码后重新构建,再重启 dsh web。
更新日志
- 1.6.0:新增「允许删除对话」标签页;开启后左侧对话栏的「…」菜单多出红色的「删除对话」,「视图选项」菜单多出红色的「删除所有已归档」,二次确认后彻底删除对话(连同子代理会话)。设置文件路径的
DSH_HOME支持~/…写法。插件各功能单独容错,DSH 接口变化时只影响对应功能、不影响对话和其余功能。Host/Client 协议号升为 8,更新后需重启dsh web。 - 1.5.0:中文输入法下输入框开头的
、(及全角/)等效于/,可直接弹出提示词模板菜单。 - 1.4.1:修复在
/模板名后用中文输入法(如 QQ 拼音)输入时,拼音重复插入、文字变蓝且无法删除的问题。/模板不再占用 DSH 输入框的「命令认领」,改为发送时识别;模板名含空格时也能正确匹配。 - 1.4.0:新增「环境变量」标签页,提示词模板和全局提示词中的
{{env:变量名}}在发送前替换为对应的值;WebDAV 新增「同步环境变量文件」开关,支持合并配置文件(同名不同值逐个选择)、本地覆盖云端、云端覆盖本地。 - 1.3.0:自定义全局提示词保存为模板目录下的
名称.md;新增「WebDAV 云同步」标签页(服务器配置、HTTP / SOCKS5 代理、测试连接、双向选择性同步与同名覆盖确认)。 - 1.2.0:新增全局提示词(模板文件或自定义文本),对之后新建的每个对话生效;
/模板追加在全局提示词之后。 - 1.1.0:首个公开版本:
/选择提示词模板、模板编辑器、外部编辑器同步。