본문으로 건너뛰기
J

dsh-loadout

jungleyxu/dsh-loadout

Loadout for DeepSeek Harness: group tools into kits, keep only the current kit's tools visible in each conversation, and manage everything from a settings panel.

설치

dsh plugin --profile web add github:jungleyxu/dsh-loadout

README

装备栏(dsh-loadout)

装备栏图标

English · 更新日志 · MIT

「装备栏」是这个插件本身;「套装」是栏里那几套配好的工具。一个对话选一套装备,只带用得上的工具。

同名项目说明:npm 上的 dsh-loadout 是本插件唯一的发行包。GitHub 上另有两个同名的独立项目(YongjianWan/dsh-loadout、JYDW-1/dsh-loadout),作者、功能都与本插件不同。用 dsh plugin add dsh-loadout 装到的一定是本插件;从 GitHub 安装时请认准 JungleYXu/dsh-loadout。

DeepSeek Harness 的工具是全局可见的:插件装得越多,每个对话要背的工具清单越长,模型也越容易挑错工具。装备栏把工具编成套装,每个对话只留当前套装里的工具。

它做什么

能力说明
三档底座全部(不裁剪,默认)/ 仅内置(只留 DSH 自带工具)/ 我的套装
我的套装「新建套装」里勾选内置工具与插件,存成一套带名字的装备。卡片上可以设为默认、复制切换命令、编辑(改完覆盖保存,或另存成新的一套)、备注、删除
预置套装只读五套:仅聊天 / 编程用 / 查找资料 / 项目梳理 / 写作;套用即可,改动会另存为你的套装
加挂在底座之上临时加挂插件,不必为一次性组合新建套装
备注每个套装可加 20 字备注,菜单里显示成 测试1(用于视频制作)
token 账本菜单状态行 省 ~1.9k tokens,设置页给出「保留 N 个工具 · 约 X tokens · 省多少」的完整账目
刷新菜单右上 ⟳ 重拉工具目录并重套用当前选择
每对话独立每个对话把自己的选择存下来(loadout.json 的 assignments),重启后照旧;改默认只影响之后新建的对话

安装

三种来源,装的是一个东西:

# 1) npm(已发布,最短)
dsh plugin --profile desktop add dsh-loadout

# 2) GitHub(npm 不可达时用同一个包)
dsh plugin --profile desktop add github:JungleYXu/dsh-loadout

# 3) 本地目录(开发用,链接安装,不拷贝)
dsh plugin --profile desktop add link:<本目录绝对路径>

让 Harness 自己装也可以(等价于第 3 种):

plugin_manager action=install_bundle target=<本目录绝对路径>

没有 dsh CLI 的机器上,可以用 scripts/install.ps1:它建 junction 链接并写 profile manifest,等价于 install_bundle;-Copy 会退回拷贝安装,除非你清楚后果,否则别用。

装完重启应用:宿主插件没有热重载,界面至少刷新一次。

链接安装,不要拷贝

拷贝安装会把文件复制进 profile,之后源码再改,profile 里仍是旧副本,启动时跑的也是旧 bundle——0.4.0 的启动崩溃就是这样产生的。链接安装(junction)没有这个问题,preflight 会检查副本来是否漂移。

自检:

node scripts/preflight.mjs   # 校验 id/包名一致、补丁行、以及安装副本是否漂移
node scripts/selftest.mjs    # 宿主逻辑:底座/加挂/保存/存储往返/遮罩
node scripts/client-smoke.mjs # 浏览器 bundle:菜单与设置页渲染

使用

  • 标题栏「装备栏」按钮:切底座、加挂插件、新建套装。
  • 设置 → 装备栏:管理套装、改备注、选新对话默认套装。
  • /kit <名字>:在对话里直接切换。

设置页只管「新对话从哪套开始」,不切换当前对话。当前对话在标题栏「装备栏」里切,还没开始的对话可以直接在欢迎区选。页面顶部有一行说明:「设置页不绑定具体会话,上面的数字来自最近活跃的那个对话。」

「新对话默认套装」列表里,「全部 / 仅内置」与套装是同一种卡片:都能「复制切换命令」或「设为默认」。「我的套装」默认折叠,只显示当前默认的那一个(这一行下面始终留着「+ 新建套装」),点开才列出全部。已保存的套装可以在卡片上「编辑」:改动在那张卡片下面展开,可选「覆盖保存」(正在用它的对话立刻按新工具集走)或「另存为新套装」。

预置套装(五套,只读)

五套都是只读的,改动会另存为你的套装。它们只列内置工具,插件工具要靠「加挂」或另存时勾选。

套装子命令 id内置工具
仅聊天chatask_user_question weather web_search web_fetch read + 协作五件套
编程用codingpwsh read write edit glob grep todo_write + 协作五件套
查找资料researchweb_search web_fetch advanced_search research platform_search read write glob grep + 协作五件套
项目梳理planask_user_question web_search web_fetch read write edit glob grep todo_write + 协作五件套
写作writingread write edit glob grep + 协作五件套

「协作五件套」= send_message list_agents interrupt_agent subagent subagent_fork。每套都带着它们,否则子代理切到这套之后连回话都发不出来。

给模型用的工具

kit_list(列出套装、当前绑定与该套被多少对话使用)、kit_apply(把当前对话切到某套)、kit_assign(把指定对话钉到某套)、kit_rename(只改显示名)、kit_create / kit_set_tools / kit_set_default / kit_delete(增删改,kit_delete 支持 dryRun)。这些工具在任何套装里都保留,不会把自己锁死。

对话的绑定

每个对话有一条持久绑定,写在 ~/.dsh/loadout.json 的 assignments 里:

{ "assignments": { "session-abc": "coding", "session-def": null } }

null 就是「全部」。规则:

  • 对话第一次出现时写入一次:有新默认就记默认,从别的对话分叉出来就继承父对话,升级前就存在的旧对话记 null(保持当时的实际状态)。
  • 由默认写下的绑定同时记进 auto,读取时不算「这个对话选过它」,胶囊显示当前默认;第一轮对话结束后标记消失,绑定才真正属于这个对话。
  • 只要对话还没发出第一条消息,欢迎区以胶囊为准:胶囊显示什么,下一条消息就用什么;不一致时由宿主把工具集对齐(不写草稿,仍算「借来的」)。
  • 只有显式切换(标题栏、/kit、kit_apply、kit_assign)才改绑定,显式选择会清掉 auto 标记。
  • 改「新对话默认套装」只影响之后新建的对话,已有对话各自保留。
  • 子代理不记录绑定:它的父对话已经决定了它能看见什么。

升级

  • 0.5.x → 0.12.x:包名没变(dsh-loadout),直接 plugin_manager install_bundle 覆盖即可;套装数据在 ~/.dsh/loadout.json,第一次启动会迁移到 schema: 2(旧文件备份为 .v1.bak-<时间戳>)。

  • 0.4.x → 0.12.x:包名从 dsh-plugin-kits 改成 dsh-loadout,先卸旧的再装新的:

    plugin_manager action=remove_bundle target=dsh-plugin-kits
    plugin_manager action=install_bundle target=<本目录绝对路径>
    

    旧数据 ~/.dsh/plugin-kits.json 会被读一次并迁移到 ~/.dsh/loadout.json。

  • 任何版本升级后都重启应用:宿主代码没有热重载,只刷新界面不够。

token 账本

菜单的状态行会说 当前:编程 · 省 ~1.9k tokens,设置页顶部给出完整账目:

当前对话保留 18 个工具 · 约 1.1k tokens · 「全部」约 3.0k tokens · 本套装省约 1.9k tokens

每个套装卡片也标着自己的估价(≈1.1k tokens)。算法是估算:把每个工具的定义(名字、说明、参数 schema)序列化后除以 3.5 字符/token,只算工具清单这一段——不含对话历史、系统提示或模型缓存的影响。界面上都写了「估算」,不要拿它当账单。「当前对话保留」这一侧直接量的是运行时真正投给模型的 schema,「全部」那一侧来自工具层的定义表。

切换的代价

换套装不是免费的。请求前缀的固定顺序是「系统提示 → 工具定义 → 历史消息 → 本轮输入」,缓存按前缀命中;工具定义一变,它后面那一整段历史在下一个请求里就要按未命中价重算一遍。所以代价按上下文长度算,收益按轮数算:

  • 开场就选好:几乎不花钱,而且之后每轮都省。
  • 15 万 token 的对话中途切一次:约等于「整段上下文未命中一次」的钱(本机实测 ¥0.2 量级),之后要靠每轮省下的那 1.9k 慢慢还。
  • 空闲很久、刚重启、刚压缩过:缓存本来已经冷了,这时候切基本免费。

菜单里每一行右边显示的是这一行会往提示词里放多少工具定义(≈3.1k tokens、全部 是 ≈5.6k tokens、仅内置 是 ≈4.1k tokens),套装的默认标记跟在名字后面(全部(默认)、idea(默认));状态行带 · 上下文 ~150k。行上不写钱:换任何一套,被作废的都是同一段历史,那个数字对每一行都一样,区别在「这套装进多少工具」。钱只在菜单底部「重要提醒」下面那一行出现,加粗:

本对话上下文(约 150k tokens),下一条未命中价≈¥0.23,之后恢复至缓存价。

这个数字来自宿主 token meter 的 contextPressure 投影(提供方报告的上一次请求规模,加上之后新增的表面增量)。价格按 ¥2 / ¥0.5 每百万 token(未命中 / 命中)估算——只用于提示,不影响切换本身。设置页里 全部 与 仅内置 两张卡片也写着各自的 token 数,和套装卡片一样。

加挂 分组的数量写成 已在对话中 / 插件总数(例如 加挂(1/5))——所以在「全部」底座下它是 6/6:六个插件都已经在对话里,没有可再加的。分组标题的悬停提示只说加挂的语义(「在当前选择上临时加开插件,不新建套装。已经包含的插件不能再加。」),账在底部那一行。

对话页菜单底部那段说明的最后一条是重要提醒:建议新建对话时就直接选好套装;对话中途切换会改变插件清单,下一条消息按未命中价重算一次,之后恢复。长对话影响较大,短对话基本无影响;空闲很久、刚压缩过、刚重启之后再切,基本免费。

选默认套装不在对话页——它属于全局决定,在 设置 → 装备栏 里改(每张卡片的「设为默认」),改完之后新建的对话从这套开始,当前对话不受影响,也不会触发任何重算。

三个入口各管一段,互不越界:设置页管全局(默认套装、新建 / 编辑 / 删除套装);新对话的胶囊只管这一条还没开始的对话(选哪套,不碰默认,也不建不改);对话页菜单只管当前对话(切换、新建套装)。

在对话页「+ 新建套装」里配好之后有两个按钮:保存(只存起来,当前对话继续用它原来的工具)和保存并启用(存下来并立刻切过去——这会改变插件清单,悬停提示会说明下一条消息要按未命中价重算一次)。设置页里的「保存」永远只是保存,那里不切换当前对话。

新建对话时选套装

空白对话的输入框工具行里、标准模式选择栏右侧,多一个 选择对话套装 <当前选择> ▾ 的小胶囊。鼠标停上去显示「这个新对话会用「X」套装」;选过之后显示「这个对话会用「X」套装,下一条消息生效」。点开是一个向上浮出的折叠列表:全部、仅内置、预设套装、我的套装——后两组默认收起,点开才列出条目;收起时仍会显示「当前默认的那一套」(名字后面标 (默认),与标题栏一致)。列表里的 全部 / 仅内置 / 每套套装都按标题栏的规则标 (默认);胶囊本身不标,它说的是「这个对话实际用哪套」。设过默认就显示那套,没设过就显示「全部」。选中的那套从第一条消息起生效;发出第一条消息后整个胶囊不再出现。

这是唯一真正免费的切换——此时还没有 agent,也就没有任何请求前缀被建起来,工具定义怎么写都不影响缓存。所以在这里挑,比发完第一条消息再切便宜。

选择只绑在当前这条对话上,不动「新对话默认套装」;想改默认仍然去设置页。如果宿主版本没有这个插槽(比如更早的 DSH),这一行不会出现,其余功能不受影响。

还没有发出第一条消息就离开这个新对话(点了别的对话、或刷新页面),这个选择会被当作草稿丢掉:下次再进新对话回到默认套装,和 DSH 自己把模式重置回「标准模式」是同一件事。草稿标记存在 loadout.json 的 drafts 里,所以重启 DSH 不会把「上次随手选的」误当成这条对话的决定。真正发过一条消息之后,它才成为这个对话的固定套装。

在设置里改了默认套装,回到欢迎区显示的就是新的默认,不需要重新打开对话。

文件

  • 套装与绑定:~/.dsh/loadout.json(schema: 2;kits 是你的套装,assignments 是每个对话的绑定,defaultKit 是新对话默认值)。
  • 从 0.11.x 升到 0.12.0 时自动迁移一次,旧文件备份为 loadout.json.v1.bak-<时间戳>。
  • 旧版 ~/.dsh/plugin-kits.json 只读一次,随后迁移。
  • 卸载:plugin_manager action=remove_bundle target=dsh-loadout。

已知边界

  • 本插件只控制「对话看得到哪些工具」。已启用的插件仍在后台运行;界面插件(侧边栏、主题等)不受影响。
  • 看不到「已下载但未启动」插件的工具:工具只在插件 apply() 后存在。
  • 遮罩是「减法」:内置工具用工具层的 deny 规则隐藏,插件工具用 tools.restrict({ deny })。
  • 新装(或删掉 ~/.dsh/loadout.json 后)默认是「全部」;覆盖安装会保留你已选的默认套装。

排查

应用起不来,弹窗写 web boot: … entry did not activate / dsh-loadout: import failed

这是客户端 bundle 没注册成工厂导致的,启动审计对任何没激活的条目都是致命的。按顺序看:

  1. node scripts/preflight.mjs —— 它会直接告诉你 bundle 的工厂 id 和包名是否一致。不一致就是这一类问题。
  2. 确认安装的是链接不是拷贝:preflight 会打印 as a link (no copy to drift);如果是拷贝,删掉 node_modules/dsh-loadout 重新 install_bundle。
  3. 仍不行就 remove_bundle 后重装并重启,别在 profile 里手工改 package.json / cordis.patch.yml。

怀疑装到了同名插件

  • 看 profile 的 package.json:dsh-loadout 的值是 link: / file:,还是 registry 版本。
  • 打开 node_modules/dsh-loadout/package.json,repository.url 应当是 github.com/JungleYXu/dsh-loadout。不是,就说明装的是另一个同名项目。
  • 两个同名包不能共存——都占 dsh-loadout 这一个 key,后装的会覆盖先装的。先 plugin_manager action=remove_bundle target=dsh-loadout,再按本文顶部的三种方式重装。

装了但界面没有按钮 / 工具没生效

  • 宿主代码要重启应用才加载;浏览器 bundle 至少刷新一次。
  • GET /dsh-loadout/state 能返回 JSON 说明宿主在跑;404 说明宿主没加载成功。
  • 菜单里点 ⟳ 可以重拉工具目录,不用重启(只对已加载的宿主有效)。

切换套装后工具全没了

0.7/0.8 的 allow 名单缺陷已修,现在只做 deny 遮罩。若仍遇到,点「全部」即可恢复,并把 /dsh-loadout/debug 的输出留一份。

관련 플러그인