dsh-skill-picker 0.3.2 → 0.3.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
@@ -102,6 +102,8 @@ DSH 的 [dsh-tool-skill](https://github.com/deepseek-ai/deepseek-harness) 在 `a
102
102
 
103
103
  ## 更新日志
104
104
 
105
+ - **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)
106
+ - **v0.3.3**:兜底扫描对齐官方全部技能根——补扫 user-agents 层(`~/.agents/skills`,含 `DSH_AGENTS_HOME`),扫描顺序与官方 rank 一致(项目级优先于用户级);走兜底时 ⚡ 面板显示「本地扫描」来源徽标便于排障(对应 issue #5)
105
107
  - **v0.3.2**:安装文档修正——实测三种安装方式并补网络特例(HTTPS 受限改 SSH、npm 新版本 24h 内被 minimumReleaseAge 门禁挡旧版的规避方法)
106
108
  - **v0.3.1**:README 顶部新增一键快速安装命令(`dsh plugin --profile web add dsh-skill-picker`)与 npm 版本/许可徽章
107
109
  - **v0.3.0**:拼音搜索——`/` 补全与 ⚡ 面板的搜索目标加入技能名/描述的拼音全拼(带空格+连打)与首字母索引,中文技能可拼音直搜(如 `ji yi` →「备份记忆」)
@@ -112,8 +114,9 @@ DSH 的 [dsh-tool-skill](https://github.com/deepseek-ai/deepseek-harness) 在 `a
112
114
 
113
115
  ## 兼容性与注意事项
114
116
 
115
- - **技能来源**:**优先走官方宿主 skills API**(`connection.api.skills.list`——与 DSH 内置 `/` 补全**完全同一个数据源**,会话作用域,自动覆盖用户级 `~/.dsh/skills`、项目级 `<workspace>/.dsh/skills`、`<workspace>/.agents/skills` 等全部官方目录);官方 API 不可用时**自动回退**到内置扫描(用户级 + 项目级目录)。两条路都支持 `DSH_HOME` 环境变量。
116
- - **暂不扫描**:`~/.agents/skills` 与自定义技能目录(`customSkillDirs` 配置)——需要的话欢迎 PR。
117
+ - **技能来源**:**优先走官方宿主 skills API**(`connection.api.skills.list`——与 DSH 内置 `/` 补全**完全同一个数据源**,会话作用域,自动覆盖全部官方目录);官方 API 不可用时**自动回退**到内置扫描。两条路都支持 `DSH_HOME` 环境变量。
118
+ - **兜底扫描范围**:与官方 `dsh-skill-filesystem` provider 的默认根完全同源——项目级 `<workspace>/.dsh/skills`、`<workspace>/.agents/skills`,用户级 `~/.dsh/skills`、`~/.agents/skills`(`$DSH_AGENTS_HOME` 可覆盖),同名时按官方 rank 项目级优先。走兜底时 ⚡ 面板底部显示「本地扫描」徽标。
119
+ - **暂不支持**:自定义技能目录(官方 `customSkillDirs` 配置)——需要的话欢迎 PR。
117
120
  - **失败保护**:client 端用 `ctx.slots.inject`(等 `conversation.input.right` 插槽声明存在才注册,插槽缺失时静默跳过,不会拖垮启动);host 端路由 try/catch,扫描目录不存在时返回空列表而非报错。
118
121
  - **依赖版本**:按 DSH `0.1.0-rc.6` API 编写(cordis 4 / web profile 标准装配)。如遇 DSH 大版本更新导致 API 变化,插件会以启动日志的插件错误提示为准,卸载 `dsh plugin --profile web remove dsh-skill-picker` 即可回退。
119
122
 
package/lib/client.js CHANGED
@@ -25900,6 +25900,23 @@ var statusStyle = {
25900
25900
  color: "var(--dsw-alias-label-tertiary, #8a94a6)",
25901
25901
  fontSize: "13px"
25902
25902
  };
25903
+ var sourceBadgeStyle = {
25904
+ display: "inline-flex",
25905
+ alignItems: "center",
25906
+ alignSelf: "flex-start",
25907
+ margin: "0 8px 8px",
25908
+ padding: "2px 8px",
25909
+ border: "1px solid rgba(255, 193, 7, 0.35)",
25910
+ borderRadius: "999px",
25911
+ background: "rgba(255, 193, 7, 0.1)",
25912
+ color: "#d9a520",
25913
+ fontSize: "11px",
25914
+ lineHeight: "16px",
25915
+ flex: "none"
25916
+ };
25917
+ var sourceBadgeTextStyle = {
25918
+ fontFamily: "var(--ds-font-family-code, ui-monospace, monospace)"
25919
+ };
25903
25920
  function BoltIcon() {
25904
25921
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", width: "16", height: "16", "aria-hidden": "true", style: { display: "block" }, children: [
25905
25922
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("linearGradient", { id: "dsh-sp-bolt-grad", x1: "0", y1: "0", x2: "0", y2: "1", children: [
@@ -25922,6 +25939,7 @@ function SkillPickerButton(props) {
25922
25939
  const [open, setOpen] = (0, import_react.useState)(false);
25923
25940
  const [skills, setSkills] = (0, import_react.useState)(void 0);
25924
25941
  const [error, setError] = (0, import_react.useState)(void 0);
25942
+ const [source, setSource] = (0, import_react.useState)(void 0);
25925
25943
  const [query, setQuery] = (0, import_react.useState)("");
25926
25944
  const [usage, setUsage] = (0, import_react.useState)(() => loadUsage());
25927
25945
  const [active, setActive] = (0, import_react.useState)(0);
@@ -25933,6 +25951,7 @@ function SkillPickerButton(props) {
25933
25951
  if (typeof props.listSkills === "function" && props.session?.sessionId !== void 0) {
25934
25952
  const listed = await props.listSkills(props.session.sessionId);
25935
25953
  setSkills(Array.isArray(listed) ? listed : []);
25954
+ setSource("official");
25936
25955
  return;
25937
25956
  }
25938
25957
  } catch (cause) {
@@ -25944,6 +25963,7 @@ function SkillPickerButton(props) {
25944
25963
  const json = await res.json();
25945
25964
  if (!json.ok) throw new Error(json.error || "bad response");
25946
25965
  setSkills(Array.isArray(json.skills) ? json.skills : []);
25966
+ setSource("host");
25947
25967
  } catch (cause) {
25948
25968
  setError(String(cause?.message ?? cause));
25949
25969
  }
@@ -26047,40 +26067,46 @@ function SkillPickerButton(props) {
26047
26067
  autoFocus: true
26048
26068
  }
26049
26069
  ),
26050
- error !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: statusStyle, children: `\u52A0\u8F7D\u5931\u8D25\uFF1A${error}` }) : skills === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: statusStyle, children: "\u52A0\u8F7D\u4E2D\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: listStyle, children: filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: statusStyle, children: "\u6CA1\u6709\u5339\u914D\u7684\u6280\u80FD" }) : filtered.map((skill, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
26051
- "button",
26052
- {
26053
- type: "button",
26054
- ref: (el) => {
26055
- itemRefs.current[index] = el;
26056
- },
26057
- onClick: () => pick(skill.name),
26058
- onMouseEnter: (event) => {
26059
- setActive(index);
26060
- event.currentTarget.style.background = "var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,0.12))";
26061
- },
26062
- onMouseLeave: (event) => {
26063
- event.currentTarget.style.background = "transparent";
26070
+ error !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: statusStyle, children: `\u52A0\u8F7D\u5931\u8D25\uFF1A${error}` }) : skills === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: statusStyle, children: "\u52A0\u8F7D\u4E2D\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
26071
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: listStyle, children: filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: statusStyle, children: "\u6CA1\u6709\u5339\u914D\u7684\u6280\u80FD" }) : filtered.map((skill, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
26072
+ "button",
26073
+ {
26074
+ type: "button",
26075
+ ref: (el) => {
26076
+ itemRefs.current[index] = el;
26077
+ },
26078
+ onClick: () => pick(skill.name),
26079
+ onMouseEnter: (event) => {
26080
+ setActive(index);
26081
+ event.currentTarget.style.background = "var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,0.12))";
26082
+ },
26083
+ onMouseLeave: (event) => {
26084
+ event.currentTarget.style.background = "transparent";
26085
+ },
26086
+ style: {
26087
+ ...itemStyle,
26088
+ ...index === active ? { background: "var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,0.12))" } : {}
26089
+ },
26090
+ children: [
26091
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: nameStyle, children: `/${skill.name}` }),
26092
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: descStyle, children: skill.description ?? "" })
26093
+ ]
26064
26094
  },
26065
- style: {
26066
- ...itemStyle,
26067
- ...index === active ? { background: "var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,0.12))" } : {}
26068
- },
26069
- children: [
26070
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: nameStyle, children: `/${skill.name}` }),
26071
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: descStyle, children: skill.description ?? "" })
26072
- ]
26073
- },
26074
- skill.name
26075
- )) })
26095
+ skill.name
26096
+ )) }),
26097
+ source === "host" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: sourceBadgeStyle, title: "\u5B98\u65B9\u6280\u80FD API \u4E0D\u53EF\u7528\uFF0C\u5217\u8868\u6765\u81EA\u672C\u5730\u76EE\u5F55\u626B\u63CF\uFF08\u4E0E\u5B98\u65B9 / \u8865\u5168\u540C\u6E90\uFF09", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: sourceBadgeTextStyle, children: "\u672C\u5730\u626B\u63CF" }) })
26098
+ ] })
26076
26099
  ] })
26077
26100
  ] });
26078
26101
  }
26079
26102
  function apply(ctx) {
26103
+ const inputTriggers = typeof ctx.get === "function" ? ctx.get("inputTriggers") : ctx.inputTriggers;
26080
26104
  const listSkills = async (sessionId) => {
26081
- const skills = ctx.connection?.api?.skills;
26105
+ const remoteSkills = ctx.remote?.skills;
26106
+ const connectionSkills = ctx.connection?.api?.skills;
26107
+ const skills = remoteSkills ?? connectionSkills;
26082
26108
  if (skills === void 0 || typeof skills.list !== "function") {
26083
- throw new Error("connection.api.skills unavailable");
26109
+ throw new Error("skills RPC unavailable (remote.skills / connection.api.skills)");
26084
26110
  }
26085
26111
  const controller = new AbortController();
26086
26112
  const { result } = await skills.list({ sessionId }, controller.signal);
@@ -26099,6 +26125,24 @@ function apply(ctx) {
26099
26125
  currentCwd = "";
26100
26126
  }
26101
26127
  };
26128
+ const fetchHostSkills = async () => {
26129
+ const cwd = typeof currentCwd === "string" && currentCwd !== "" ? `?cwd=${encodeURIComponent(currentCwd)}` : "";
26130
+ const res = await fetch(`/dsh-skill-picker/skills${cwd}`, { headers: { accept: "application/json" } });
26131
+ const json = await res.json();
26132
+ if (!json.ok) throw new Error(json.error || "bad response");
26133
+ return Array.isArray(json.skills) ? json.skills : [];
26134
+ };
26135
+ const resolveSkills = async (sessionId) => {
26136
+ try {
26137
+ return await listSkills(sessionId);
26138
+ } catch {
26139
+ try {
26140
+ return await fetchHostSkills();
26141
+ } catch {
26142
+ return [];
26143
+ }
26144
+ }
26145
+ };
26102
26146
  syncCwd();
26103
26147
  const unsubscribe = ctx.sessions.list.subscribe(syncCwd);
26104
26148
  ctx.effect(() => {
@@ -26129,7 +26173,7 @@ function apply(ctx) {
26129
26173
  };
26130
26174
  const refreshNames = async (sessionId) => {
26131
26175
  try {
26132
- const skills = await listSkills(sessionId);
26176
+ const skills = await resolveSkills(sessionId);
26133
26177
  namesCache.set(sessionId, (Array.isArray(skills) ? skills : []).map((s) => s.name));
26134
26178
  notifyLexicon(sessionId);
26135
26179
  } catch {
@@ -26140,7 +26184,7 @@ function apply(ctx) {
26140
26184
  name: "skill-fuzzy",
26141
26185
  order: -10,
26142
26186
  async candidates(session, { query, signal }) {
26143
- const skills = await listSkills(session.sessionId);
26187
+ const skills = await resolveSkills(session.sessionId);
26144
26188
  if (signal.aborted) return [];
26145
26189
  namesCache.set(session.sessionId, skills.map((s) => s.name));
26146
26190
  notifyLexicon(session.sessionId);
@@ -26190,7 +26234,11 @@ function apply(ctx) {
26190
26234
  return { text: `/${candidate.name} ` };
26191
26235
  }
26192
26236
  };
26193
- const unregister = ctx.inputTriggers.registerSource(source);
26237
+ if (inputTriggers === void 0 || typeof inputTriggers.registerSource !== "function") {
26238
+ console.warn("[dsh-skill-picker] inputTriggers service unavailable; fuzzy / completion disabled (\u26A1 panel still works)");
26239
+ return;
26240
+ }
26241
+ const unregister = inputTriggers.registerSource(source);
26194
26242
  return () => {
26195
26243
  unregister();
26196
26244
  namesCache.clear();