DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins

dsh-skill-tag

Skill Tag

给 DSH 的技能(skill)打标签,用一条布尔表达式在「工作区 / 对话」两级选出要下发给模型的技能子集

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add dsh-skill-tag@0.1.15
README兼容性版本

兼容性与来源证明

Skill Tag 以 dsh-skill-tag 发布,当前版本为 0.1.15。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
npm
Registry 更新时间
2026/11/4

版本

0.1.15stable
2026/11/3
0.1.13stable
2026/10/31
0.1.12stable
2026/10/31
查看其余 10 个版本收起版本
0.1.10stable
2026/10/31
0.1.8stable
2026/10/28
0.1.7stable
2026/10/27
0.1.6stable
2026/10/24
0.1.5stable
2026/10/24
0.1.4stable
2026/10/22
0.1.3stable
2026/10/22
0.1.2stable
2026/10/21
0.1.1stable
2026/10/21
0.1.0stable
2026/10/21

相关插件

正在加载相关插件…

最新版
0.1.15
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
531.8 kB
文件数
35
Surface
web
许可证
MIT
发布源
npm
GitHub
★ 0
周下载
1,292
最近提交
2026/11/3
查看源码 ↗项目主页 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 agents-orchestration 分类下经过校验的插件。

Headless@deepseek-ai/dsh-headlessdsh one-shot bundle:基于 dsh-base 的直接核心 Agent/Session 运行器,不包含 Host、HTTP 或浏览器层Experimental Agent Team Profile@deepseek-ai/dsh-experimental-agent-team-profile在一个实验性捆绑包中提供 Agent Teams 协作、工具和 Web UIExperimental Ralph Bundle@deepseek-ai/dsh-experimental-ralph-bundle使用全新代理执行明确请求的重复工作。Subagent Codex@deepseek-ai/dsh-subagent-codex基于官方 app-server 协议的一次性 Codex 子代理提供程序

README

dsh-skill-tag

给 DSH 的技能(skill)打标签,然后用一条布尔表达式在「工作区 / 对话」两级 选出真正要下发给模型的技能子集。

装了几十上百个 skill 之后,真正的痛点不是"怎么分类",而是"这次对话只想让模型 看到哪几个"。全部下发会撑爆上下文、也会让路由互相打架;手工一条条开关又太累。 这个插件把这件事变成一条表达式:

pdf + docx            # 只要处理文档的技能
office * plugin       # 只要 office 包带来的、且是插件相关
(aegis + tdd) * !wip  # 两个都要,且排除 wip

它能做什么

能力说明
全局标签建/改名/删除标签,标签之间无层级
标签组模板把表达式存成模板(前端组、只留本机技能…),可复用、可求值
布尔表达式+=OR、*=AND、* 优先、支持括号;改标签名会自动改写所有引用它的表达式
工作区级筛选每个工作区一套条件,作为该工作区新建对话的初始模板;改工作区不影响已存在的对话
对话级筛选每个对话有自己的快照:新建时继承一次,之后冻结;未配置过的对话 = 空条件 = 全部技能;可一键「继承工作区配置」重新拉取
禁用 / 手动添加在标签命中之外额外放行某个技能,或强行屏蔽某个(即使它命中)
技能 ↔ 标签双向编辑「tag 联系 skill」和「skill 联系 tag」两个方向都能改
打开技能所在文件夹任何列出技能的地方都有 📁 按钮,一点即在文件管理器里定位
自带一个 skill安装后你的 agent 会获得 tagging-skills,能照着文档自己给技能打标签

界面预览

全部是真实实例截图(105 个技能 / 48 个标签 / 一个开了筛选的工作区),没有 mock。 图片源文件在 images/(不进 npm 包,README 里用 jsDelivr 绝对地址,GitHub 与 npm 都能显示; 之所以不走 raw.githubusercontent.com,是因为部分网络到该域名的 IPv6 不通,图片会间歇性加载失败)。

图 1 · 入口:会话行「…」→「管理标签」

会话行菜单里的管理标签入口

对话级的配置就挂在会话行的「…」菜单最下面,不用去翻设置页。

图 2 · 入口:命令面板里的 /skill-tag-manage

命令面板中的管理对话标签命令

在输入框敲 / 也能直接开「管理对话标签」——同一个界面,省一次点菜单。

图 3 · 打标签:设置 →「Skill 标签」

Skill 标签管理页:新建标签、按来源分组的标签列表

三个页签分工明确:tag 联系 skill(一个标签包含哪些技能)、skill 联系 tag (一个技能拥有哪些标签)、编辑标签组(写表达式存模板)。 标签按来源分组(来自:aegis 23 个 Skill),每条带 tagId 和「全局删除」。

图 4 · 一个技能看它身上有哪些标签

skill 联系 tag:左列技能、右列标签勾选

左列 技能 (105) 每行标着它有几个标签;右边勾选即时保存,不用点确认。 右列会把该技能已有的标签排在列表最前面(并保持它们原来的相对顺序),所以不用滚动 去列表里找;顶部搜索框过滤之后,"已拥有优先"依然成立。 这是配条件前最常开的一页:先看清楚"这个技能到底被哪些标签命中"。

图 5 · 工作区「管理标签」:左写条件,右就是下发名单

工作区管理标签双列界面

左列是标签条件,下面实时显示解析结果和状态「有条件筛选(严格子集)」; 右列标题是 技能 (54) · 模型可见 48:前一个数是命中表达式的全部技能, 后一个数是其中真正下发给模型的那些。两者不等时标题下方会多出橙色说明, 列出差额的去向——X 个只能 /名字 手动调用(SKILL.md 里写了 disable-model-invocation: true) 和 Y 个宿主没注册(扫描到了但宿主没装),对应行也会带 仅手动 / 未注册 徽标并置灰。 还有第三种状态 暂不可判定:这一趟没拿到完整的宿主视角(比如该会话没在运行, 宿主的 skill registry 只给了全局层),此时"没返回"不等于"没注册",这些技能带灰色虚线徽标、 不置灰,也不计入 模型可见 N。这三种状态的区别与成因见下一节。 每行都可以「查看 tag」「禁用」,📁 直接打开技能所在文件夹。列表顶部的搜索框只过滤显示 (标题会变成 技能 (匹配/总数)),不改动实际下发的技能集合。底部还能「+ 精细化添加技能」 「查看被禁用的技能」。

图 6 · 对话「管理标签(对话)」:可以有自己的一份

对话级管理标签界面与继承来源

同一个工作区下的某个对话想单独收窄,就在这里改:它会记下继承来源与时间 (改动不影响工作区),左下角一键「继承工作区配置」覆盖回来。

技能注册的三种状态:已注册 / 未注册 / 暂不可判定

界面右列标题 技能 (全部) · 模型可见 N 里的 N,只数已被宿主确认注册的技能。 而"未注册"和"暂不可判定"是两件完全不同的事,别把它们读成一句:

状态判据界面表现是否计入 模型可见 N
已注册这一趟宿主快照里返回了这个名字正常显示✅ 计入
未注册宿主视角完整,但没返回这个名字置灰 + 橙色「未注册」❌ 不计入
暂不可判定这次没取到完整的宿主视角灰色虚线徽标,不置灰❌ 不计入

「宿主快照」是什么

插件判断一个技能"模型能不能看到",是去问宿主:"这条技能你注册了吗?" —— 宿主对这个问题的一次完整回答,就是一次宿主快照(宿主 ctx.skills.snapshot() 返回的技能清单)。没取到快照=这一问没有得到一个可信的完整回答, 于是那批技能既不该标"已注册"(没证据),也不该标"未注册"(更没有证据)。

为什么会取不到完整快照

宿主的技能注册表是分层的:global 层 + 按 agent / preset 展开的层。 不带 scope 去问时,宿主只回 global 层。而覆盖用户级技能根 (~/.dsh/skills、~/.agents/skills)的那些 provider 恰好注册在 agent 层里 —— 一旦 scope 传不下去,这些名字就整段不在回答里,"没返回"于是被误当成"没注册"。

这是 0.1.15 修掉的缺陷(issue #1):此前同一份本机文件,在有 live agent 的对话里是 "已注册",在没在跑的对话里就变成"宿主没注册,模型侧根本不存在"——仅仅换一个对话, 结论就翻转。

可能的触发原因,按实际出现频率排序:

  1. 该会话没有在运行——历史对话、已关闭的标签页、或刚切过来还没起 agent。 agents.list() 里找不到它,scope 传不下去,宿主只给 global 层。这时列表顶部会 标注「该会话未运行,以下为全局视角」。
  2. 会话 ID 对不上——浮层传的是 session-<uuid>,宿主 agent 暴露的 id 若是另一个 字符串(宿主侧依次取 session.id / session.header.id / agent.id),同样等于取不到 agent。
  3. 宿主自己报 complete: false——宿主明确说这一趟没收集全(某个 provider 还在加载、 某个根扫描失败)。插件直接采用宿主的结论,整批降级为不可判定。
  4. 快照抛错或并发守卫被占——snapshotSkills() 返回 null,插件退回纯文件系统自扫描; 这时它更没资格断言"宿主没注册"。
  5. 宿主确实没注册——这种情况"暂不可判定"是保守过头了。但只要拿到完整视角, 插件就会给出明确的「未注册」,不会一直挂着。

想看确定结论怎么办

  • 把对话切到正在运行的那个会话再看——它能拿到完整视角,用户级技能会恢复成"已注册"。
  • 设置页那种不带 conversationId 的口径本身就是全局视角,出现"暂不可判定"是预期行为。
  • 视角不完整时,POST /skill-tag/api/skills/purge-stale 会直接返回 409 INCOMPLETE_VIEW 而拒绝执行——因为它要靠"未注册"这个结论去删关联记录,视角不可信时删了就是误删。

安装

这是一个 DSH(DeepSeek Harness)插件,装进 profile 后随宿主启动加载。

一条命令

dsh plugin --profile <your-profile> add dsh-skill-tag

也可以不走 npm,直接从 GitHub 装:

dsh plugin --profile <your-profile> add github:aawef/dsh-skill-manager-tags

dsh plugin 本质是把参数转发给 profile 目录里的 pnpm,它不会替你启用插件 —— 装完还要在 profile 的 package.json 的 dsh.profile.bundles 数组里加上 "dsh-skill-tag":

{
  "dsh": {
    "profile": {
      "bundles": [ "...", "dsh-skill-tag" ]
    }
  }
}

重启 DSH 后生效(设置页会出现「Skill 标签」一项)。

官方 DSH 桌面端的 desktop profile 归应用独占,命令行改不动它 —— 桌面端请在 Settings → Plugins 或插件市场里安装。

依赖 Node ≥ 20;需要 DSH 0.2.0-rc.1 及以上(见 engines.dsh)。

宿主的 @deepseek-ai/cordis 声明为 peerDependency,由 DSH 本体提供,不要自己安装。

存储域 spec 由本插件 src/storage/domain-spec.js 本地构造(等价于宿主的 defineDomain / domainTable),运行时通过 ctx.storageDomain.open() 交给宿主打开,不 import 任何宿主内核包。 这样 profile 里不会出现与宿主同名的第二份模块副本:早期版本曾 import @deepseek-ai/dsh-storage-domain,宿主升级(如 0.2.1-rc.2 → rc.3)时那份旧副本会被判定 不兼容并拒绝加载,导致存储域整体不启动、会话与工作区列表全空(详见 CHANGELOG 2026-10-24)。

本地开发式安装

运行时加载的是 profile 里 node_modules/<插件名> 那一份,不是你的源码目录。两种接法:

  • junction(推荐):让 ~/.dsh/profiles/web/my-plugins/<插件名> 指向你的源码目录, pnpm 会把它硬链接进 node_modules —— 三个路径是同一个 inode、同一份文件, 改完内容立刻生效,不需要复制;只有新增/删除文件才要在 profile 里重跑一次 pnpm install。
  • 物理拷贝:如果当初是手动 cp 进去的,那就是两份独立文件,改完必须再 cp 一次并校验哈希:
S=/path/to/your/dsh-skill-tag
D=~/.dsh/profiles/<profile>/node_modules/dsh-skill-tag
cp "$S/lib/client.js" "$D/lib/client.js"
sha256sum "$S/lib/client.js" "$D/lib/client.js" | cut -c1-16

生效规则(很重要,搞混了会以为"改了没用"):

文件改完之后
lib/client.js刷新页面即可(宿主 no-cache 加载)
lib/index.js、src/**、cordis.patch.yml重启桌面进程

怎么用

1. 打标签

设置 →「Skill 标签」,三个按钮:

  • tag 联系 skill —— 选一个标签,勾它包含哪些技能
  • skill 联系 tag —— 选一个技能,勾它拥有哪些标签
  • 编辑标签组 —— 写表达式、存成模板

界面长什么样见上方「图 3」「图 4」。

也可以让 agent 自己做:安装后 tagging-skills 这个 skill 会出现在技能列表里, 里面是完整的推导法 + API 调用纪律。不要照抄文档里的标签名(aegis、office-skills 只是某一台机器上的实例),按自己机器的技能清单推导。

或者直接跑一键脚本(先 dry-run):

node tools/auto-tag.mjs http://127.0.0.1:<PORT>                  # 只看计划,不落地
node tools/auto-tag.mjs http://127.0.0.1:<PORT> --apply          # 落地来源轴
node tools/auto-tag.mjs ... --domain ./my-domain-map.json --apply # 连领域轴一起

脚本幂等,重跑应输出 tagsCreated: 0。

2. 给工作区/对话配条件

  • 工作区:侧栏工作区行的「…」菜单 →「管理标签」
  • 对话:会话行的「…」菜单(图 1),或聊天框左下角「+」→「管理对话标签」(图 2)

左列写标签条件表达式,右列是命中结果(可手动添加 / 禁用 / 恢复)。改完立刻生效: 该对话下发给模型的技能列表就是右列里模型可见的那一份(标题的第二段数字)。 两张双列界面见「图 5」「图 6」。

3. 表达式语法

语法含义
A + BOR
A * BAND
(A + B) * C括号;* 优先级高于 +
"my tag"标签名含空格或 + * ( ) 时加引号(所以起名请用 kebab-case)
空串无条件 = 全部技能(合法的"不筛选")

表达式会被编译成 DNF({orTags, andGroups})再交给匹配引擎。 标签改名时,所有引用它的表达式会被按 token 原位改写,不会失效。

4. 路由型技能(router)别被拆散

有些技能的职责就是「帮你挑别的技能」——description 里出现 Use when the user asks… Router over the skills… 这类措辞,还常带有 skill-filesystem 之类的下挂资源。

插件只按名字屏蔽,从不改任何 SKILL.md 的内容,所以路由器自己的提示词一个字都不会变。 会坏的是依赖关系:路由器可见、而它指向的技能被屏蔽时,模型照着提示去加载只会拿到 skill "X" is not available for model invocation —— 这一轮路由白跑,还可能让模型反复试。

所以配条件时请把路由器和它的簇当成一个整体(用簇标签一次圈住,别按领域标签零散地挑):

你想要的表达式
整个簇(路由器 + 7 个 office 技能)routes:office
只要路由器本身,不要成员router * routes:office
只要成员,不要路由器(自己挑,不需要分发提示)routes:office * !router

插件运行时不认识"谁是路由器"(router 只是标签约定),这一层耦合靠表达式自己保证完整。 选完可以用 POST /skill-tag/api/workspaces/skills 回读 candidates,确认路由器的簇成员没被拆散。

HTTP API

挂在 /skill-tag/api 前缀下。所有路由同时接受 GET 和 POST (q(k) = body[k] ?? url.searchParams.get(k)),所以浏览器直接敲 URL 也能调试。 响应一律是 JSON;出错是 {ok:false, error, code}。

路由方法主要参数说明
/stateGET—全量快照:tags / skillLinks / templates / workspaces / conversations
/skillsGETcwd可打标签的技能清单(带 path)。带 cwd 才能扫到项目级 .dsh/skills
/skills/tagsGET/POSTskillName, tagIds读/写一个技能拥有的标签。写是整体覆盖,见下方警告
/skills/revealPOSTskillName 或 path拉起文件管理器定位该技能
/tagsPOSTtagId, name建标签(同名会报 already exists)
/tags/renamePOSTtagId, name改名并同步改写所有表达式
/tags/linkPOSTskillId, tagIds关联
/tags/unlinkPOSTskillId解除关联
/tags/deletePOSTtagId全局删除,返回 affectedWorkspaces
/templates/validateGET/POSTexpression校验 → {ok, errors, normalized, dnf, config}
/templates/evaluateGET/POSTtemplateId求值(要 templateId,不是 expression)
/templates/expandGET/POSTtemplateId展开
/templates /templates/update /templates/deletePOST—模板 CRUD
/workspaces/expressionPOSTworkspaceId,

警告:/skills/tags 的写是整体覆盖。写之前必须先 GET 读回旧 tagIds 再合并,否则会清掉已有标签。这是最容易踩的坑。

目录结构

lib/index.js         宿主侧入口(ESM):注册 filter provider / storage domain / HTTP API / 自带 skill
lib/client.js        浏览器侧界面(__ModuleLoader__ 工厂,无构建步骤)
src/domain/
  expr.js            表达式引擎:tokenize / parse / toDnf / dnfToConfig / validateExpression
  match.js           匹配:matches / candidatesFor
  origin.js          来源轴推导(从 path 推出这个技能是哪个包带来的)
  conv.js            对话四区状态(加载/豁免/未加载/屏蔽)
  workspace.js       工作区视图
src/api.js           HTTP 路由
src/service.js       服务层(CRUD + 改名同步 + 迁移)
src/storage/         storage domain schema + repository
src/reveal.js        拉起系统文件管理器
skills/tagging-skills/  随插件注册给宿主的一个 skill
tools/auto-tag.mjs      一键打标签脚本(dry-run 默认)
tests/               node:test,194 项
docs/                设计文档(见下)
images/              README 用的界面截图(不进 npm 包)

文档

文件内容
docs/给技能打标签.md打标签的完整手册:三轴设计、API 调用序列、错误对照表、交付检查清单
docs/标签组配置.md表达式编辑页的设计与验收
docs/管理标签.md工作区双列配置
docs/对话管理标签.md对话级配置与继承
docs/技能发现与宿主对齐.md为什么"插件看到的技能数"曾经少于宿主,以及怎么对齐
docs/deny-shadow-覆盖率.md为什么包带来的技能会静默漏网(path 为 null 的那批),以及宿主对影子候选的强校验、层/rank 语义
docs/修复指南.md「打了标签技能却没出现」的排查手册:三层判定树、transcript 逐帧解压取证、预设关闭默认技能根(A 层)、已知假阳性、升级回归清单
docs/skill联系tag与打开文件夹.md两个方向编辑 + 📁 按钮
CHANGELOG.md按日期倒序的完整开发记录

开发

npm install
npm test          # 194 项

测试分两类,都在 tests/ 下:

  • *.test.mjs —— 单元/领域测试,被 node --test 自动收集
  • __*.mjs —— 手工脚本:HTTP 全链路冒烟(__api-smoke)、用迷你 React 运行时 执行真实组件并断言渲染结构(__page-render 等)

两个 glob 都要,npm test 已经配好;只跑 tests/**/*.test.mjs 会漏掉 46 个用例。

界面预览可以在没有浏览器扩展时生成静态 HTML:

node docs/preview/render-preview.mjs     # → docs/preview/标签组配置-preview.html

(产物已在 .gitignore 里排除。)

改插件时的几条硬约束

这几条都是踩过的坑,源码注释里也写了:

  • 宿主服务是声明式注入的:没写进 export const inject 的服务,ctx.<service> 读属性会直接抛 cannot get property "x" without inject。而且 ctx.x ?? ctx.get("x") 这种兜底照样抛 —— 左侧求值时就炸了。只能 ctx.get() + try/catch。
  • 注册给宿主的回调里抛异常 = host fatal(整个桌面进程挂掉)。 domain/changed 监听器、provider 的 list/get、apply() 同步栈,全部 self-contain。
  • 新增 HTTP 路由必须重启桌面进程,否则新路由 404 —— 这个 404 极易被误判成 "路由名写错了"。
  • ctx.effect(() => fn()) 的 thunk 立刻执行,它的返回值才是 dispose。 写成 ctx.effect(() => domain.close()) 会在装配阶段就把 domain 关掉。

许可证

MIT — 见 LICENSE。

expression
改工作区条件
/workspaces/skillsGETworkspaceId读该工作区的候选项(candidates)
/workspaces/skills/add remove disable enablePOSTworkspaceId, skillName手动添加/移除/禁用/恢复
/conversations/configGET/POSTconversationId对话双列配置的读/写
/conversations/inheritPOSTconversationId整体覆盖式继承所属工作区配置
/conversations/candidatesGETconversationId该对话实际生效的技能列表