Skip to content

Releases: aawef/dsh-skill-manager-tags

2026-10-24 — 0.1.6:管理标签界面三处改进(技能搜索框 / 窗口定高 / 已有标签优先)

Choose a tag to compare

@github-actions github-actions released this 24 Oct 11:24

本次发布 v0.1.6 · npm: https://www.npmjs.com/package/dsh-skill-tag/v/0.1.6

本节是 0.1.6 的发布说明,细节见下面三节。

  1. 「管理标签」右列新增技能搜索框(工作区版 / 对话版共用)。只剩一个输入框和
    「清除」按钮,标题在有搜索词时变成 技能 (匹配/总数),让人看得出是被过滤了。
    搜索只过滤显示:view.visible、手动添加、禁用、继承、实际下发给模型的集合全都不变。
    无匹配时给「没有匹配「xxx」的技能。」,与「本来就没有技能」区分开。
  2. 浮层高度固定为 82vh。第一版只写了 maxHeight,搜索越准、行数越少、窗口就越小,
    每敲一个字母窗口抖一下。定高之后只有中间列表滚动,边框与底部按钮位置不动。
  3. 「skill 联系 tag」右列把该技能已有的标签排到最前面,稳定分区、组内保持原序,
    和搜索可叠加;勾选态与写库内容都不受影响。

验证

三处各有渲染级测试:tests/__manage-render.mjs(13 项,含搜索过滤与高度不变)、
tests/__page-render.mjs(23 项,含排序与搜索叠加、ownedTagsFirst 纯函数)。
其余测试文件全绿;skilltags.test.mjs 中 5 条涉及 mkdtemp/spawn 的用例在本机
沙箱下以 EPERM 失败,与本次改动无关(未触碰 src/)。


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

资产:dsh-skill-tag.tgz(sha256 ea61aa020a0ba2ee34ae879f7f9d8edee8101a9bdd66462a5cd6344d80eab65b)
稳定地址:https://github.com/aawef/dsh-skill-manager-tags/releases/latest/download/dsh-skill-tag.tgz

2026-10-24 — 0.1.5:解除对宿主内核包 `dsh-storage-domain` 的依赖

Choose a tag to compare

@github-actions github-actions released this 24 Oct 10:11

本次发布 v0.1.5 · npm: https://www.npmjs.com/package/dsh-skill-tag/v/0.1.5

现象

DSH 升级到 0.2.1-rc.3 后,侧栏所有工作区与对话消失。数据本身没有丢
(~/.dsh/storages/workspace.json 与 ~/.dsh/sessions/** 都完好),是界面读不到。

根因

本插件此前 import { defineDomain, domainTable } from "@deepseek-ai/dsh-storage-domain"。
为了让这个裸导入能解析,安装时 profile 的 package.json 里必须再装一份
@deepseek-ai/dsh-storage-domain 实体 —— 而宿主 app.asar 里本来就内置了同名模块。
于是同一个进程里出现同一模块的两份不同版本:

  • profile 里那份被钉在 0.2.1-rc.2;
  • 宿主升级成 0.2.1-rc.3 后,运行时按 includePrerelease 的 semver 校验判定旧副本不兼容,
    直接拒绝加载(见 profiles/desktop/.plugin-manager/logs/*/pnpm.log);
  • 存储域整体未启动 → ctx.storageDomain 缺失 → workspace/session 列表为空。

同一结构问题在 0.2.0-rc.2 时代已经发生过一次,当时的处置是「把依赖从插件挪到 profile +
留一句升级时记得同步改版本号的人工提醒」。升级 rc.3 时没人同步 → 原样复发。只要插件还持有
宿主内核包的副本,每次宿主升级都必然再炸一次。

做法

  1. 新增 src/storage/domain-spec.js,本地实现 defineDomain / domainTable:
    • 宿主的 defineDomain 实际只做「校验 + 原样返回 spec」,domainTable 只是 { valueSchema }
      包装,复刻成本极低;
    • 校验规则与宿主一致(UNIT_NAME_RE = /^[a-z][a-z0-9_]*$/、version 非负整数、
      compatibleVersions 低于 version、global.schema 不接受 null 等)。
  2. src/storage/schema.js 改为从本地模块引入;宿主能力仍走 ctx.storageDomain.open(skillTagDomain),
    这部分本来就没变。
  3. 删除 peerDependencies["@deepseek-ai/dsh-storage-domain"] —— 插件对宿主内核包零 import,
    不再需要任何声明。
  4. engines.dsh 由版本白名单(0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || >=0.2.1 <1.0.0)
    改为半开区间 >=0.2.0-rc.1 <1.0.0。

为什么改成半开区间就不会再漏

宿主校验用的是 semver.satisfies(runtimeVersion, range, { includePrerelease: true }),
预发布版本参与比较;并且插件只要写了 engines.dsh,peerDependencies 完全不参与判定
(宿主源码 unmetRequirements)。所以一个半开区间即可覆盖 0.2.1-rc.3、未来的 rc.4、
0.2.2-rc.1 以及 0.2.1 正式版,不必每次宿主发版都回来改白名单。

用宿主自带 semver 实测:

✅ >=0.2.0-rc.1 <1.0.0  vs 0.2.1-rc.3   / 0.2.1-rc.4 / 0.2.2-rc.1 / 0.2.1
❌ 旧白名单              vs 0.2.1-rc.3   (这就是被拒的原因)

升级建议

强烈建议从 0.1.4 升级到 0.1.5:0.1.4 在 DSH ≥ 0.2.1-rc.3 上会被判不兼容而跳过后台加载,
并可能连带让整个存储域无法上台。装完 0.1.5 后,profile 里不再需要
@deepseek-ai/dsh-storage-domain 这一行,可以删掉(若之前手动加过)。

验证

  • 本仓库测试 194/194 通过;
  • 宿主 profile 重装后 @deepseek-ai/ 顶层只剩 cosmokit / schemastery 两个通用库,
    不再有任何内核包副本;
  • 升级后工作区与历史对话正常显示。

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

资产:dsh-skill-tag.tgz(sha256 fb62c076472f1561ca449a6c82c7c16d1d9f6c5ee55c3916db8fb87cd66fe66f)
稳定地址:https://github.com/aawef/dsh-skill-manager-tags/releases/latest/download/dsh-skill-tag.tgz

2026-10-22(补)— 修复:`/被屏蔽的技能名` 会让界面卡死(provider.get 自递归)

Choose a tag to compare

@github-actions github-actions released this 22 Oct 08:51

本次发布 v0.1.4 · npm: https://www.npmjs.com/package/dsh-skill-tag/v/0.1.4

现象

用户敲一轮 /<某个被屏蔽的技能>(例如 /office-docx),agent 的 pre-step 永不 resolve,
界面像卡住一样,没有任何报错。

根因

userInvocable:false 并不能挡住这条路径 —— dsh-tool-skill 的 agent/pre-step 对
/name 手势是先 ctx.skills.get(name) 再判 isUserInvocable(宿主源码 line 909)。
而宿主 registry.get() 的流程是:

collect() → 选中胜出候选(agent 层里就是我们的影子)→ 调影子自己的 provider.get()

我们的 provider.get() 原来会 upstream.getDefinition() → 又回到 registry.get(name)
→ 又选中影子 → 又是我们 …… 每轮都是 await,栈不涨、Promise 永不 settle,
所以表现为静默挂死而不是爆栈。实测探针里 registry.get 被反复调用到人为设的上限。

补一句:模型侧走 skill 工具不会撞上这个坑 —— 那边先 list() 找 summary,
影子 modelInvocable:false 会直接抛 not available for model invocation(line 874-875)。
只有 /name 手势(人打的)才先 get。

改动

lib/index.js createFilterProvider.get():被 denied 命中的名字就地返回 deny 定义
(content: ""),不再回问宿主。本来影子也没有正文可给,所以对外行为不变,只是断掉回环。

测试

  • 新增两条:get 被屏蔽的名字时上游 getDefinition/summaries 调用次数必须为 0,
    且返回的定义能过宿主 validateDefinition 的形状校验;放行的名字照旧拿到真正文。
  • 全量 194 通过 0 失败。
  • 探针(probe-get-loop.mjs):修前 registry.get 调用 6 次(人为截断),修后 0 次。

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

资产:dsh-skill-tag.tgz(sha256 90b31c9e8bd10e0bedc37a9ecd0f4504c64922948cba39c8a54160214e9a2162)
稳定地址:https://github.com/aawef/dsh-skill-manager-tags/releases/latest/download/dsh-skill-tag.tgz

2026-10-22 — 修复:包/插件带来的技能永远屏蔽不掉

Choose a tag to compare

@aawef aawef released this 22 Oct 08:08

现象

模型侧技能目录 71 条,插件口径 universe 105 / visible 54 / denied 51。
按名字对账:71 − 17 = 54 = visible,恰好 17 个该屏蔽的技能原样漏给模型。

漏网名单干净得可疑 —— 全部是"不由文件系统技能根提供"的那些:

  • aegis 4(using-aegis / update-aegis / writing-skills / communicating-concisely)
  • mattpocock 4(ask-matt / research / teach / writing-for-agents)
  • archify 1、本插件的 tagging-skills 1、office 7(path 为 null)

被成功屏蔽的 34 个则全部来自 ~/.dsh/skills + ~/.agents/skills。
日志、右列界面、/skill-tag/api/skills 都是对的(105),只有模型侧少影子 ——
所以这是静默失效,长期没被发现。

根因

createFilterProvider.list() 只遍历 upstream.summaries() 的结果发影子,
即发射范围 = 本次能看到的 summary。而 createUpstreamAdapter.summaries() 在
nesting.depth > 0 时会整段跳过宿主快照,只剩扫盘结果;守卫之所以被占住,是因为
apply() 里 createUpstreamAdapter 与 createSkillDiscoverer 共用同一份
snapshotGuard
—— 设置页 / UI 快照 / 另一个 agent 的收集 / 并发刷新任一在飞,
否认路径就被连坐降级。

于是"看得见"和"看不见"的分界线,恰好就是"文件系统根"和"别的 provider"的分界线。

改动

lib/index.js:

  1. 影子按名字发(根治):list() 分两段 —— 看得见的照抄 summary(保留
    path / 描述);看不见的直接构造最小合法候选。屏蔽按键名生效,不依赖可见性。
  2. 两个出口各用一份重入计数器:递归那圈只在 adapter 内部,
    createSkillDiscoverer 不在回路里,共用只会互相连坐;发现器另加同 key 并发合并。
  3. 观测(方案 C):发生补发时 warn 一行 shadow backfill ... backfilled=N,
    降级时附 hostSnapshot=skipped(nested);健康路径不刷日志。

顺带修正:

  • 影子 rank 统一取 0。原来照抄上游 rank,office 是 BUNDLED_SKILL_RANK=600,
    同层里若有 rank 更小的同名候选就会被顶掉(层内按 rank 升序去重)。
  • 上游 summaries() 抛错时不再 return [](那等于把全部技能放行),改为只用
    denied 补影子。
  • denied 里的历史脏名字先过宿主的 kebab-case 校验:一个不合法候选会让
    整个 provider.list() 抛错,宿主随即丢弃本插件的全部贡献。