dsh-skill-tag 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -181,6 +181,26 @@ node tools/auto-tag.mjs ... --domain ./my-domain-map.json --apply # 连领域轴
181
181
  表达式会被编译成 DNF(`{orTags, andGroups}`)再交给匹配引擎。
182
182
  标签改名时,**所有引用它的表达式会被按 token 原位改写**,不会失效。
183
183
 
184
+ ### 4. 路由型技能(router)别被拆散
185
+
186
+ 有些技能的职责就是「帮你挑别的技能」——description 里出现 *Use when the user asks…*
187
+ *Router over the skills…* 这类措辞,还常带有 `skill-filesystem` 之类的下挂资源。
188
+
189
+ 插件**只按名字屏蔽,从不改任何 `SKILL.md` 的内容**,所以路由器自己的提示词一个字都不会变。
190
+ 会坏的是**依赖关系**:路由器可见、而它指向的技能被屏蔽时,模型照着提示去加载只会拿到
191
+ `skill "X" is not available for model invocation` —— 这一轮路由白跑,还可能让模型反复试。
192
+
193
+ 所以配条件时请**把路由器和它的簇当成一个整体**(用簇标签一次圈住,别按领域标签零散地挑):
194
+
195
+ | 你想要的 | 表达式 |
196
+ | --- | --- |
197
+ | 整个簇(路由器 + 7 个 office 技能) | `routes:office` |
198
+ | 只要路由器本身,不要成员 | `router * routes:office` |
199
+ | 只要成员,不要路由器(自己挑,不需要分发提示) | `routes:office * !router` |
200
+
201
+ 插件运行时不认识"谁是路由器"(router 只是标签约定),这一层耦合靠表达式自己保证完整。
202
+ 选完可以用 `POST /skill-tag/api/workspaces/skills` 回读 `candidates`,确认路由器的簇成员没被拆散。
203
+
184
204
  ## HTTP API
185
205
 
186
206
  挂在 `/skill-tag/api` 前缀下。所有路由**同时接受 GET 和 POST**
@@ -114,6 +114,12 @@ skill-explorer / skills-manager 的 UI 快照、另一个 agent 的收集、`dom
114
114
  2. `recomputeDenials` 的 `universe` 在降级那一趟仍然偏窄(`denied` 靠
115
115
  `computeDeniedSkillIds` 兜底把已打标签的补了回来)。未打标签、且这一趟看不见的
116
116
  技能不会被判 deny —— 下一趟正常快照会纠正,且不影响已知名单。
117
- 3. `provider.get()` 对影子名字会回问宿主 `get`,而宿主选中的又是影子自己 →
117
+ 3. ~~`provider.get()` 对影子名字会回问宿主 `get`,而宿主选中的又是影子自己 →
118
118
  理论上可自递归。实际不可达(影子两个 invocation 都是 false,模型与 `/命令`
119
- 都调不到),暂不改。
119
+ 都调不到),暂不改。~~
120
+ **2026-10-22 更正:可达,已修。** 模型侧走 `skill` 工具确实调不到(先 `list()`,
121
+ 影子 `modelInvocable:false` 直接抛错),但 `dsh-tool-skill` 的 `agent/pre-step`
122
+ 对 `/name` 手势是**先 `get` 再判 `userInvocable`**(line 909),于是人打一轮
123
+ `/<被屏蔽的技能名>` 就会触发 `registry.get → provider.get → registry.get`
124
+ 的无限回环(async,栈不涨、Promise 不 settle → 静默卡死)。
125
+ 现在 `get()` 对 denied 名字直接返回 deny 定义(`content: ""`),不再回问宿主。
package/lib/index.js CHANGED
@@ -154,23 +154,35 @@ export function createFilterProvider(sessionId, upstream, denied, ensureFresh, l
154
154
  },
155
155
  async get(candidate, options) {
156
156
  await freshen(options);
157
- let base;
158
- try {
159
- base = await upstream.getDefinition(candidate, options);
160
- } catch {
161
- return undefined;
162
- }
163
- if (!base) return undefined;
157
+ // 被屏蔽的名字:**绝不**再回问宿主。
158
+ //
159
+ // 2026-10-22 实测的坑:宿主 registry.get(name) 的流程是
160
+ // collect() → 选中胜出候选(在我们的 agent 层里就是这条影子)
161
+ // → 调**影子自己的** provider.get(candidate)
162
+ // 而我们原先在这里调用 upstream.getDefinition → hostRegistry.get(name)
163
+ // → 又选中影子 → 又是我们 → registry.get 被无限反复调用。
164
+ // 一轮 `/<被屏蔽的技能名>` 就能撞上:dsh-tool-skill 的 agent/pre-step
165
+ // 对 `/name` 手势是**先 get 再判 invocation**(lib 内 line 909),
166
+ // 所以这条路真实可达,后果是 pre-step 永不 resolve(界面卡住)。
167
+ //
168
+ // 影子本来就没有正文可给(DENY + content:""),直接造定义即可;
169
+ // 形状必须满足宿主 validateDefinition:name/description/source/provider
170
+ // 非空字符串、invocation 是对象、content 是字符串。
164
171
  if (denied.has(candidate.name)) {
165
172
  return {
166
- ...base,
173
+ name: candidate.name,
174
+ description: `[blocked by skill-tag] ${candidate.description || candidate.name}`,
167
175
  provider: PROVIDER_NAME,
168
- description: `[blocked by skill-tag] ${base.description ?? candidate.description}`,
176
+ source: PROVIDER_SOURCE,
169
177
  invocation: DENY,
170
178
  content: "",
171
179
  };
172
180
  }
173
- return base;
181
+ try {
182
+ return await upstream.getDefinition(candidate, options);
183
+ } catch {
184
+ return undefined;
185
+ }
174
186
  },
175
187
  };
176
188
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-skill-tag",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "给 DSH 的技能(skill)打标签,用一条布尔表达式在「工作区 / 对话」两级选出要下发给模型的技能子集",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",