dsh-skill-injector
Auto-inject chosen skills into DeepSeek Harness (DSH) sessions: each prompt or once at session start, with a settings page and a composer indicator.
- Stars
- 0
- Language
- TypeScript
- Created
- Sep 6, 2026
- Updated
- Sep 6, 2026
Introduction
dsh-skill-injector · DSH 技能自动注入插件
🪡 在 DeepSeek Harness (DSH) 会话中自动注入所选技能——每轮提示注入,或仅在会话开始时注入一次。 设置页 + 输入区指示条,即点即存,重启保留。
English | 简体中文(本文档)
✨ 它解决什么问题
DSH 的技能(~/.agents/skills/ 下的 SKILL.md)平时是惰性的——只有你手动 /ponytail 或模型自己调用时才加载。如果你希望某个技能常驻生效(比如 ponytail 的极简风格、i-have-adhd 的输出结构),就得每轮手动敲一遍。
这个插件让你勾选一次,永久生效:
设置页勾选 ponytail + i-have-adhd
│ 即时写入 settings 传输层(无保存按钮,点下即存盘)
宿主半块
├─ each-prompt 模式:技能正文进系统提示,每次请求重新渲染
└─ start-only 模式:会话开始时盖一条持久消息(按会话日志去重,绝不重复)
│
浏览器半块
├─ 设置 → 技能注入:勾选列表 + 注入方式单选 + 缺失技能提示
└─ 输入框下方指示条:技能:ponytail, i-have-adhd · 每轮(5s 刷新)
📋 功能
| 功能 | 说明 |
|---|---|
| ⚙️ 设置页 | 设置 → 技能注入:技能勾选列表(带描述)、注入方式单选、缺失技能提示 |
| 🪧 输入区指示条 | 聊天输入框下方一行,实时显示当前生效的技能与模式 |
| 🔁 两种注入模式 | each-prompt(系统提示 section,每轮重渲染)/ start-only(会话开始盖戳一次) |
| 📚 活注册表 | 技能正文从 ctx.skills 实时读取,不拷贝文件——改技能立即生效,删技能优雅降级 |
| 🧩 子代理一致 | 注入同样作用于 subagent,全 agent 树语气统一 |
| 🌏 中英双语 | 设置页与指示条跟随界面语言(zh/en) |
| ♨️ 重启保留 | 真实 profile 插件:装一次,每次 DSH 启动自动加载 |
✅ 优点(以及为什么)
- 零污染注入:
start-only用官方skill-invocation消息来源盖戳,转写界面能正确标识"这是注入的技能上下文",且按会话日志去重——resume 旧会话不会重复注入。 - 活注册表,无副本:技能文件改动立即反映到下一次请求;文件被删只列为"缺失",其余技能不受影响,恢复即自动回来。
- 配置走官方 settings 传输层:即点即存、revision fence 防并发写坏、与其他设置同源持久化(
~/.dsh/settings.yaml)。 - API 全部对照源码验证:针对 DSH 0.1.3 逐个核实(settings owner scope、skills 带 cwd+scope 查找、
agent.inject消息形状、agent/session-start载荷)——不是照抄可能过时的教程。 - 宿主半块零 @deepseek-ai 运行时依赖:只依赖
schemastery;官方逻辑(renderSkillContent等)内联复刻且字节级同形。npm 发布版与源码版的 API 漂移不会弄坏它。 - 端到端实测通过:typecheck ✓ · 8/8 单测 ✓ · 构建产物 ✓ · HTTP 快照路由返回真实技能目录 ✓。
⚠️ 缺点与限制(读清楚再装)
- 兼容范围窄:只在 DSH 0.1.3-alpha.1(源码版) 上验证过。DSH 是 developer preview,API 随时可能变;升级 DSH 后若设置页/注入失灵,回来查 release notes。
- token 成本是线性的:选中的技能正文每轮请求都会发送(系统提示或首条消息)。选 3 个大技能 = 每轮多几千 token,没有缓存优化。只勾真正想常驻的。
start-only不追溯旧会话:切换模式或改勾选后,已开着的会话不会补盖戳——只对之后新开的会话生效(设计如此:按日志去重)。- 技能列表依赖会话目录:设置页的"可用技能"来自当前会话的技能目录;空白会话(hero 阶段)可能只看到全局层技能,进入会话后才是完整的。
- 无每技能开关粒度:只有全局"每轮 / 仅开始"一个模式,不能给不同技能配不同模式(想要?提 issue)。
- 手动安装稍繁琐:profile 启用 pnpm 供应链策略(
minimumReleaseAge)时dsh plugin add会被 lockfile 校验拦住,需走 README 里的手动三步(一次性,2 分钟)。 - 无遥测/自动更新:装了就是装了,DSH 升级后需要你自己回来检查兼容性。
🔧 兼容性
| 环境 | 状态 |
|---|---|
| DSH 0.1.3-alpha.1(源码 checkout 运行) | ✅ 实测通过 |
| DSH 0.1.2-rc.x(npm 发布版) | ⚠️ 未验证,客户端类型来自 rc.1 但注入逻辑未在此版本跑过 |
| DSH Desktop | ⚠️ 理论兼容(普通插件不依赖桌面专属 service),未实测 |
🚀 安装
方式一是纯 GitHub 直装——lib/ 构建产物已提交进仓库,且带 git 安装安全的 prepare 脚本(构建失败时自动回退到已提交的产物,绝不阻断安装)。
方式 A:从 GitHub 直装(推荐)
dsh plugin --profile web add github:countossbot/dsh-skill-injector
pnpm ≥10 第一次会拒绝运行 git 依赖的构建脚本,安装可能失败并提示授权。在 profile 目录的 pnpm-workspace.yaml 加:
allowBuilds:
dsh-skill-injector: true
然后重新执行上面的 add 命令。(不授权也不影响使用——prepare 会检测到无法构建,直接采用仓库里已提交的 lib/ 产物。)
安全建议:锁定 commit,让后续推送无法悄悄改变实际运行的代码:
dsh plugin --profile web add github:countossbot/dsh-skill-injector#<commit-sha>
方式 B:dsh plugin add 本地路径(先 clone 再装)
git clone https://github.com/countossbot/dsh-skill-injector.git
cd dsh-skill-injector && npm install # prepare 自动构建;或 npm run build
dsh plugin --profile web add /path/to/dsh-skill-injector
dsh --profile web --dump-config | grep skill-injector # 出现插件行即成功
方式 C:手动挂载(dsh plugin add 被供应链策略拦截时)
profile 启用了 pnpm minimumReleaseAge 时 dsh plugin add 会被 lockfile 校验拦截(任何当天发布的第三方包都触发,与本插件无关)。手动三步:
# 1. 编辑 ~/.dsh/profiles/web/package.json:
# dependencies 加 "dsh-skill-injector": "link:/path/to/dsh-skill-injector"
# dsh.profile.bundles 数组追加 "dsh-skill-injector"
# 2. profile 目录安装
cd ~/.dsh/profiles/web && pnpm install --prefer-offline --config.minimumReleaseAge=0
# 3. 重启 DSH 宿主进程
装好后:重启 DSH → 设置 → 技能注入 → 勾选技能 → 开新会话生效。
验证安装
# 路由探针(宿主半块活体证据)
curl -s http://127.0.0.1:3080/skill-injector/api | python3 -m json.tool
# → {"ok":true,"mode":"each-prompt","selected":[...],"available":[...真实技能...],"missing":[]}
# 持久化探针(设置页勾选后)
grep -A5 skill-injector ~/.dsh/settings.yaml
⚙️ 配置
无配置文件。全部在设置页操作,存于 skill-injector settings 命名空间:
| 键 | 默认 | 说明 |
|---|---|---|
mode | each-prompt | each-prompt(系统提示,每轮)/ start-only(仅会话开始) |
selected | [] | kebab-case 技能名,最多 16 个,自动去重 |
FAQ
- 为什么没有保存按钮? 设计如此——每次点击即时写入 settings 传输层(与官方设置页同一机制),点下即持久化。
- 改配置后对已开会话生效吗?
each-prompt下一次请求即生效;start-only只对新会话生效。 - 技能文件被删? 自动剔除注入并列为缺失;恢复文件后自动回来。
- token 成本怎么算? 选中的技能正文每轮都发。一个 2KB 的技能 ≈ 每轮多 ~600 token。
🔒 安全
- 对技能注册表只读:不写、不改名、不删除技能文件。
- 注入内容是本地受信 markdown;不访问网络、无遥测。
each-prompt路径对技能正文做{{转义,避免严格插值抛错;start-only消息保留原文。
🧱 架构
dsh-skill-injector/
├── src/
│ ├── index.ts # 宿主半块:settings 命名空间、技能缓存、注入逻辑、HTTP 路由
│ ├── helpers.ts # 纯函数(零 dsh import):校验/转义/渲染 → node:test 可直接测
│ └── client/
│ ├── index.tsx # 浏览器半块:settings.section + composer.dock 两个 slot
│ └── locales.ts # zh/en 字典
├── scripts/build.mjs # esbuild 双产物:node ESM + client CJS 闭包工厂
├── scripts/prepare.mjs # git 安装安全钩子:构建失败时回退到已提交的 lib/
├── tests/helpers.test.mjs # node:test 单测
├── cordis.patch.yml # dsh.bundle patch
└── package.json # dsh.bundle + dsh.client 声明
数据流细节(点开)
浏览器半块 (lib/client.js, ModuleLoader 闭包工厂)
├─ settings.section 插槽 → 设置页
│ 读/写走 settingsScope 镜像 = settings 传输层(响应式、revision fence)
└─ conversation.composer.dock 插槽 → 指示条
5s 轮询 GET /skill-injector/api,失败降级显示
宿主半块 (lib/index.js, Host 进程)
├─ settings 命名空间 "skill-injector" (mode + selected)
├─ ctx.skills.get(name, {cwd: agent.session.header.cwd, scope: agent})
│ ↑ 必须带 agent 的 cwd+scope,否则只见全局层
├─ each-prompt: systemPrompt.section(每次组装重渲染,{{ 转义)
├─ start-only: agent/session-start 时 agent.inject()
│ source={kind:'skill-invocation',name,form:'instructions'};按 snapshotEvents() 去重
└─ webServer 路由 GET /skill-injector/api → 只读 JSON 快照
🛠️ 开发
npm install
npm run build # 构建 lib/
npm run watch # 监视重建
npm test # 构建 + 单测
npm run typecheck # tsc --noEmit
客户端产物形状与官方 tsdown.client.ts 一致(window.__ModuleLoader__.load({id, factory})),外部依赖按 dsh.client.inject 声明从浏览器模块表解析。
🙏 致谢
- Zenjibad/skill-injector-plugin —— 功能蓝本;本插件针对运行中的 DSH 源码重写并修正了不兼容点(详见下)
- DeepSeek Harness —— 插件运行时
与蓝本的主要差异
| 点 | 蓝本 | 本插件 |
|---|---|---|
| 运行时依赖 | @deepseek-ai/dsh-skill/dsh-llm npm 包 | 零(内联复刻,字节级同形输出) |
| 设置写入 | 自建 PUT /api/config 路由 | 官方 settingsScope 镜像(revision fence、响应式) |
| API 依据 | 可能过时的文档/类型 | 对照 0.1.3 源码逐个验证 |