← Back to home@mikulo

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

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 异常,可以:

  1. 卸载插件:dsh plugin --profile web remove @mikulo/dsh-prompt-switcher,再启动 dsh web。设置和环境变量文件保存在 ~/.dsh/,不会丢失。
  2. 或者用不含第三方插件的 profile 启动做对比,例如 dsh --profile rescue(profile 里只列 @deepseek-ai/dsh-base 和 @deepseek-ai/dsh-web-app 两个 bundle)。

插件的 Browser 半会随页面热更新,Host 半必须重启 dsh web 才会加载新代码。两边版本不一致时,设置页顶部会提示“请重启 dsh web”。

使用

  1. 打开 设置 → 提示词模板,点击「选择文件夹」,选中存放模板的目录。
    • 只读取该目录第一层的 *.md 文件,扩展名不区分大小写。列表按文件名显示。
    • 新目录中的模板默认全部关闭,打开开关才会激活。
    • 目录里的文件增删改之后,点击「刷新」。
  2. 新建对话,在输入框输入 /,已激活的模板(例如 代码审查)会出现在菜单里。继续输入文字可以过滤。
  3. 选中模板(或直接输入 /代码审查 再按空格)后,输入框为 /代码审查 ,接着输入第一条消息并发送。发送时插件识别开头的模板名并绑定模板,之后这个对话的每一轮都会遵守该模板。
    • /代码审查 以普通文字显示(不再变成蓝色命令词)。这是为了绕开 DSH 输入框的一个问题:命令词高亮会打断中文输入法的组字,导致拼音重复、文字变蓝且无法删除。
    • 中文输入法状态下按 / 键会输入 、。输入框开头的 、(或全角 /)会自动换成 /,同样弹出模板菜单,两者等效。句子中间的 、(如“苹果、香蕉”)不受影响。

仓库里的 examples/ 有两个示例模板,可以把它设为模板目录来体验。

全局提示词

在 设置 → 提示词模板 → 全局提示词 中配置:

  1. 选择来源:
    • 模板文件:从模板目录的 .md 文件中选一个,文件名(不含扩展名)就是全局提示词的名称。之后更换模板目录不影响已选的文件。
    • 自定义文本:填写名称(必填)和内容,点击「保存」或按 Ctrl+S。内容保存为模板目录下的 名称.md(需要先配置模板目录),之后也会出现在模板列表里,可以用「编辑」修改。目录中已有同名的其他文件时,会先询问是否覆盖。改名后保存会写入新文件,旧文件保留。
  2. 打开「启用全局提示词」开关。配置不完整时开关打不开,并提示原因。

规则:

  • 只对开启之后新建的顶层对话生效;已开始的对话和子代理会话不会被注入。分叉出来的会话沿用原会话的状态(原会话有就有,没有就没有)。
  • 对话开始时绑定的是当时的快照:之后修改或关闭全局提示词,只影响再之后新建的对话。上下文压缩后会重新注入同一份快照。
  • 在新对话中用 / 选择模板时,会话里依次是 全局提示词 → 模板 → 你的第一条消息。模板追加在全局提示词之后,两者同时生效。/ 菜单中的模板说明会显示“追加在全局提示词「…」之后”。
  • 全局提示词启用了但读不到(文件被删、内容为空等)时,设置页显示原因,新对话照常进行,只是不注入。

WebDAV 云同步

在 设置 → 提示词模板 顶部切换到「WebDAV 云同步」标签页:

  1. 服务器:填写存放模板的 WebDAV 目录地址(例如 https://dav.jianguoyun.com/dav/prompts/)、用户名和密码(坚果云等服务请使用应用专用密码)。密码只保存在本机,不会回传到页面;再次保存时密码框留空表示保持不变。
  2. 代理(默认关闭):打开「通过代理连接」后,可选 HTTP 或 SOCKS5,地址默认 127.0.0.1:7891,可以修改。开关决定「测试连接」和同步是否经过代理。暂不支持需要认证的代理。
  3. 测试连接:用表单里当前(可以尚未保存)的配置访问该目录,显示成功、认证失败、目录不存在、代理连不上等结果。
  4. 点「保存配置」后才能同步。

同步(只处理两边目录第一层的 .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 自带的对话菜单只能归档,不能删除。本插件可以加上删除功能:

  1. 打开 设置 → 提示词模板,切换到「允许删除对话」标签页,打开「是否允许删除对话」开关(默认关闭)。
  2. 在左侧对话栏把鼠标移到对话标题上,点击出现的「…」图标。菜单在「置顶对话 / 重命名 / 分叉对话 / 归档对话」之后多出红色的「删除对话」。
  3. 点击「删除对话」后弹出确认框「是否删除对话,删除不可撤销」,点「是」才会删除,点「否」取消。

删除时插件会:

  • 先按「归档对话」的官方流程停止该对话正在运行的工作(当前轮次、后台任务、子代理、定时任务)。如果你正在查看这个对话,页面会自动离开它。
  • 卸载该对话在当前 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:首个公开版本:/ 选择提示词模板、模板编辑器、外部编辑器同步。

许可证

MIT