dsh-skill-picker 0.3.3 → 0.4.0
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 +18 -1
- package/lib/client.js +21 -80
- package/lib/client.js.map +2 -2
- package/package.json +1 -1
- package/src/client/index.jsx +34 -97
package/README.md
CHANGED
|
@@ -98,10 +98,27 @@ DSH 的 [dsh-tool-skill](https://github.com/deepseek-ai/deepseek-harness) 在 `a
|
|
|
98
98
|
[DSH] agent/pre-step 识别手势 → 自动加载技能 → 执行
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
- client 半:注册到官方 `conversation.input.right` 插槽(composer 工具行、发送按钮左侧的控件位),**技能列表优先走官方宿主 skills API**(`
|
|
101
|
+
- client 半:注册到官方 `conversation.input.right` 插槽(composer 工具行、发送按钮左侧的控件位),**技能列表优先走官方宿主 skills API**(`remote.skills.list`——与 DSH 内置 `/` 补全同源,会话作用域,自动含用户级/项目级技能),失败时回退到 host 扫描路由;插入文本走框架输入机的 `inputActions.setDraft`(单一路径,撤销/草稿持久化自动处理);最近/常用排序 + 拼音索引(`pinyin-pro`)在 client 侧生成,按技能缓存
|
|
102
|
+
|
|
103
|
+
## 与官方 `/` 补全的关系(v0.4.0 起:增强,而非并列)
|
|
104
|
+
|
|
105
|
+
**v0.2.0–0.3.4**:插件注册了一个独立的 `/` 候选源(`skill-fuzzy`),与官方 ui-skill 源**并列**——菜单里出现两个技能分组,搜索行为相互独立(冲突风险、视觉重复)。
|
|
106
|
+
|
|
107
|
+
**v0.4.0 起**:**不再注册平行源**。改为给官方 `@deepseek-ai/dsh-client-ui-skill` 包的 candidates **打补丁**——其候选逻辑从 `skill.name.startsWith(query)`(前缀匹配)换成调用插件注入的全局函数 `window.__dshSkillPickerFuzzy`(fuzzysort 模糊 + pinyin-pro 拼音 + 最近/常用排行,与 ⚡ 面板同一套规则)。
|
|
108
|
+
|
|
109
|
+
**效果**:官方「技能」分组**仍是唯一一个 `/` 技能列表**(官方规则全部保留:`userInvocable` 区分、菜单文案、排序基础),只是匹配行为被升级;插件不再产生第二列表。
|
|
110
|
+
|
|
111
|
+
**Patch 部署姿势**(本机 `profiles/web/local/`,与其它 patch 插件同款):
|
|
112
|
+
1. 把官方包拷到 `profiles/web/local/dsh-client-ui-skill/`(`lib/client.js` 改 candidates 为 `window.__dshSkillPickerFuzzy` 优先,`startsWith` 兜底)
|
|
113
|
+
2. profile package.json 加依赖 `"@deepseek-ai/dsh-client-ui-skill": "link:C:/Users/<user>/.dsh/profiles/web/local/dsh-client-ui-skill"`
|
|
114
|
+
3. `pnpm install` 后重启 DSH
|
|
115
|
+
|
|
116
|
+
> 官方 DSH 升级后(如 alpha.6),若 ui-skill 包变动:重拷新包覆盖 local 副本、重打补丁即可;插件本身逻辑不变。
|
|
102
117
|
|
|
103
118
|
## 更新日志
|
|
104
119
|
|
|
120
|
+
- **v0.4.0**:**单列表模糊搜索**——不再注册独立 `/` 候选源,改为 patch 官方 ui-skill 的 candidates(模糊+拼音注入,官方列表是唯一来源,无并列列表、无搜索冲突);实测 `/jiyi` → 官方「技能」组 backup-memory 排第一
|
|
121
|
+
- **v0.3.4**:适配 DSH 0.1.2-alpha.5 —— 技能列表改用官方 `remote.skills` RPC(alpha.5 将 rc.x 的 `connection.api.skills` 改名),`/` 补全与 ⚡ 面板统一「官方 RPC → host 扫描兜底」;`dsh.client.inject` 声明 `dsh-client-ui-input-trigger`(alpha.5 装载器只给声明了提供者的插件暴露 `inputTriggers` 服务)。修复 alpha.5 下 `/` 模糊/拼音搜索失效(实测 `/jiyi` → backup-memory)
|
|
105
122
|
- **v0.3.3**:兜底扫描对齐官方全部技能根——补扫 user-agents 层(`~/.agents/skills`,含 `DSH_AGENTS_HOME`),扫描顺序与官方 rank 一致(项目级优先于用户级);走兜底时 ⚡ 面板显示「本地扫描」来源徽标便于排障(对应 issue #5)
|
|
106
123
|
- **v0.3.2**:安装文档修正——实测三种安装方式并补网络特例(HTTPS 受限改 SSH、npm 新版本 24h 内被 minimumReleaseAge 门禁挡旧版的规避方法)
|
|
107
124
|
- **v0.3.1**:README 顶部新增一键快速安装命令(`dsh plugin --profile web add dsh-skill-picker`)与 npm 版本/许可徽章
|
package/lib/client.js
CHANGED
|
@@ -26101,9 +26101,11 @@ function SkillPickerButton(props) {
|
|
|
26101
26101
|
}
|
|
26102
26102
|
function apply(ctx) {
|
|
26103
26103
|
const listSkills = async (sessionId) => {
|
|
26104
|
-
const
|
|
26104
|
+
const remoteSkills = ctx.remote?.skills;
|
|
26105
|
+
const connectionSkills = ctx.connection?.api?.skills;
|
|
26106
|
+
const skills = remoteSkills ?? connectionSkills;
|
|
26105
26107
|
if (skills === void 0 || typeof skills.list !== "function") {
|
|
26106
|
-
throw new Error("connection.api.skills
|
|
26108
|
+
throw new Error("skills RPC unavailable (remote.skills / connection.api.skills)");
|
|
26107
26109
|
}
|
|
26108
26110
|
const controller = new AbortController();
|
|
26109
26111
|
const { result } = await skills.list({ sessionId }, controller.signal);
|
|
@@ -26139,87 +26141,26 @@ function apply(ctx) {
|
|
|
26139
26141
|
};
|
|
26140
26142
|
}, "dsh-skill-picker: composer input slot");
|
|
26141
26143
|
ctx.effect(() => {
|
|
26142
|
-
const
|
|
26143
|
-
|
|
26144
|
-
|
|
26145
|
-
|
|
26146
|
-
|
|
26147
|
-
|
|
26148
|
-
}
|
|
26149
|
-
|
|
26150
|
-
|
|
26151
|
-
|
|
26152
|
-
|
|
26153
|
-
|
|
26154
|
-
|
|
26155
|
-
|
|
26156
|
-
namesCache.set(sessionId, (Array.isArray(skills) ? skills : []).map((s) => s.name));
|
|
26157
|
-
notifyLexicon(sessionId);
|
|
26158
|
-
} catch {
|
|
26159
|
-
}
|
|
26160
|
-
};
|
|
26161
|
-
const source = {
|
|
26162
|
-
trigger: "/",
|
|
26163
|
-
name: "skill-fuzzy",
|
|
26164
|
-
order: -10,
|
|
26165
|
-
async candidates(session, { query, signal }) {
|
|
26166
|
-
const skills = await listSkills(session.sessionId);
|
|
26167
|
-
if (signal.aborted) return [];
|
|
26168
|
-
namesCache.set(session.sessionId, skills.map((s) => s.name));
|
|
26169
|
-
notifyLexicon(session.sessionId);
|
|
26170
|
-
const ordered = rankByUsage(skills, loadUsage());
|
|
26171
|
-
const q = String(query ?? "").trim().toLowerCase();
|
|
26172
|
-
if (q === "") {
|
|
26173
|
-
return ordered.map((s) => ({ name: s.name, description: s.description }));
|
|
26174
|
-
}
|
|
26175
|
-
const targets = ordered.map((s) => ({
|
|
26176
|
-
s,
|
|
26177
|
-
search: `${s.name} ${s.description ?? ""} ${skillPinyinText(s.name, s.description ?? "")}`
|
|
26178
|
-
}));
|
|
26179
|
-
const results = fuzzysort_default.go(q, targets, {
|
|
26180
|
-
key: "search",
|
|
26181
|
-
limit: 12,
|
|
26182
|
-
threshold: -1e4
|
|
26183
|
-
});
|
|
26184
|
-
return results.filter((r) => r.score > 0).map((r) => ({ name: r.obj.s.name, description: r.obj.s.description }));
|
|
26185
|
-
},
|
|
26186
|
-
warm(session) {
|
|
26187
|
-
refreshNames(session.sessionId);
|
|
26188
|
-
},
|
|
26189
|
-
lexicon(session) {
|
|
26190
|
-
return namesCache.get(session.sessionId);
|
|
26191
|
-
},
|
|
26192
|
-
subscribeLexicon(session, listener) {
|
|
26193
|
-
const key = session.sessionId;
|
|
26194
|
-
const set = lexiconListeners.get(key) ?? /* @__PURE__ */ new Set();
|
|
26195
|
-
set.add(listener);
|
|
26196
|
-
lexiconListeners.set(key, set);
|
|
26197
|
-
return () => {
|
|
26198
|
-
const cur = lexiconListeners.get(key);
|
|
26199
|
-
if (cur !== void 0) {
|
|
26200
|
-
cur.delete(listener);
|
|
26201
|
-
if (cur.size === 0) lexiconListeners.delete(key);
|
|
26202
|
-
}
|
|
26203
|
-
};
|
|
26204
|
-
},
|
|
26205
|
-
onPick({ candidate }) {
|
|
26206
|
-
try {
|
|
26207
|
-
const usage = loadUsage();
|
|
26208
|
-
const name = candidate.name;
|
|
26209
|
-
const next = { ...usage, [name]: { count: (usage[name]?.count ?? 0) + 1, lastUsed: Date.now() } };
|
|
26210
|
-
saveUsage(next);
|
|
26211
|
-
} catch {
|
|
26212
|
-
}
|
|
26213
|
-
return { text: `/${candidate.name} ` };
|
|
26214
|
-
}
|
|
26144
|
+
const fuzzyMatch = (skills, query = "") => {
|
|
26145
|
+
const ordered = rankByUsage(skills, loadUsage());
|
|
26146
|
+
const q = String(query).trim().toLowerCase();
|
|
26147
|
+
if (q === "") return ordered;
|
|
26148
|
+
const targets = ordered.map((s) => ({
|
|
26149
|
+
s,
|
|
26150
|
+
search: `${s.name} ${s.description ?? ""} ${skillPinyinText(s.name, s.description ?? "")}`
|
|
26151
|
+
}));
|
|
26152
|
+
const results = fuzzysort_default.go(q, targets, {
|
|
26153
|
+
key: "search",
|
|
26154
|
+
limit: 12,
|
|
26155
|
+
threshold: -1e4
|
|
26156
|
+
});
|
|
26157
|
+
return results.filter((r) => r.score > 0).map((r) => r.obj.s);
|
|
26215
26158
|
};
|
|
26216
|
-
|
|
26159
|
+
window.__dshSkillPickerFuzzy = fuzzyMatch;
|
|
26217
26160
|
return () => {
|
|
26218
|
-
|
|
26219
|
-
namesCache.clear();
|
|
26220
|
-
lexiconListeners.clear();
|
|
26161
|
+
if (window.__dshSkillPickerFuzzy === fuzzyMatch) delete window.__dshSkillPickerFuzzy;
|
|
26221
26162
|
};
|
|
26222
|
-
}, "dsh-skill-picker: fuzzy / source");
|
|
26163
|
+
}, "dsh-skill-picker: fuzzy matcher for official / source");
|
|
26223
26164
|
}
|
|
26224
26165
|
return module.exports; } });
|
|
26225
26166
|
//# sourceMappingURL=client.js.map
|