@sparkelf/dsh-client-ui-skill-center 0.2.0-rc.14 → 0.2.0-rc.16

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/lib/client.js CHANGED
@@ -4,11 +4,11 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react_jsx_runtime = require("react/jsx-runtime");
7
8
  let react = require("react");
8
9
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
- let react_jsx_runtime = require("react/jsx-runtime");
10
10
  //#region \0dsh-css:/root/projects/deepseek-harness-plus/packages/plus/ui-skill-center/src/client/SkillCenterPage.module.css.mjs
11
- const css = ".DGiSta_page{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);flex-direction:column;align-items:center;gap:32px;padding:28px clamp(24px,4vw,48px) 48px;display:flex;overflow:auto}.DGiSta_page>*{width:100%;max-width:960px}.DGiSta_pageHead{justify-content:space-between;align-items:flex-start;gap:16px;display:flex}.DGiSta_pageTitle{margin:0;font-size:20px;font-weight:500;line-height:28px}.DGiSta_pageIntro{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:13px;line-height:20px}.DGiSta_toolbar{justify-content:flex-end;align-items:center;gap:16px;display:flex}.DGiSta_iconButton{width:28px;height:28px;color:var(--dsw-alias-label-caption);cursor:pointer;background:0 0;border:0;border-radius:28px;justify-content:center;align-items:center;display:inline-flex}.DGiSta_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.DGiSta_iconButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.DGiSta_iconWrap{display:inline-flex}.DGiSta_button{border:.5px solid var(--dsw-alias-border-l3);height:32px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:0 12px;font-size:13px}.DGiSta_button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.DGiSta_button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.DGiSta_search{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);width:100%;height:36px;color:var(--dsw-alias-label-primary);background:0 0;border-radius:10px;padding:0 12px;font-size:13px}.DGiSta_search::placeholder{color:var(--dsw-alias-label-tertiary)}.DGiSta_group{margin-top:32px}.DGiSta_groupTitle{align-items:baseline;gap:8px;margin:0 0 4px;font-size:15px;font-weight:500;line-height:22px;display:flex}.DGiSta_groupCount{color:var(--dsw-alias-label-tertiary);font-size:13px;font-weight:400}.DGiSta_groupHint{color:var(--dsw-alias-label-caption);font-size:12px;font-weight:400}.DGiSta_cards{margin:0;padding:0;list-style:none}.DGiSta_card{border-radius:12px;min-width:0;margin:0 -8px}.DGiSta_card:hover{background:var(--dsw-alias-interactive-bg-hover)}.DGiSta_cardHead{align-items:center;gap:14px;padding:8px;display:flex}.DGiSta_cardIcon{border:.5px solid var(--dsw-alias-border-l3);width:48px;height:48px;color:var(--dsw-alias-label-secondary);border-radius:10px;flex:none;justify-content:center;align-items:center;display:inline-flex}.DGiSta_cardMain{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.DGiSta_cardTitle{text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:500;line-height:20px;overflow:hidden}.DGiSta_cardDesc{color:var(--dsw-alias-label-secondary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:13px;line-height:18px;display:-webkit-box;overflow:hidden}.DGiSta_cardWhenToUse{color:var(--dsw-alias-label-caption);font-size:12px;line-height:18px}.DGiSta_cardMarks{flex-wrap:wrap;gap:6px;margin-top:2px;display:flex}.DGiSta_mark{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-tertiary);border-radius:4px;padding:1px 6px;font-size:11px;line-height:16px}.DGiSta_markOn{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.DGiSta_markOff{border-color:var(--dsw-alias-border-l4);color:var(--dsw-alias-label-caption)}.DGiSta_markDanger{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.DGiSta_cardEnd{flex:none;align-items:center;gap:8px;display:flex}.DGiSta_switch{background:var(--dsw-alias-border-l3);cursor:pointer;border:none;border-radius:10px;width:36px;height:20px;transition:background .12s;position:relative}.DGiSta_switch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.DGiSta_switchOn{background:var(--dsw-alias-state-success-primary)}.DGiSta_switch:after{content:\"\";background:var(--dsw-alias-bg-layer-1);border-radius:50%;width:16px;height:16px;transition:transform .12s;position:absolute;top:2px;left:2px}.DGiSta_switchOn:after{transform:translate(16px)}.DGiSta_dialog{width:min(560px,100%)}.DGiSta_form{flex-direction:column;gap:12px;display:flex}.DGiSta_field{flex-direction:column;gap:4px;display:flex}.DGiSta_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}.DGiSta_input{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);height:32px;color:var(--dsw-alias-label-primary);background:0 0;border-radius:8px;padding:0 10px;font-size:13px}.DGiSta_textarea{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);min-height:80px;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-markdown-code-block-small);resize:vertical;background:0 0;border-radius:8px;padding:8px 10px;font-size:12px}.DGiSta_status{text-align:center;color:var(--dsw-alias-label-tertiary);padding:32px 0;font-size:13px}.DGiSta_statusError{color:var(--dsw-alias-state-error-primary)}";
11
+ const css = ".DGiSta_page{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);flex-direction:column;align-items:center;gap:32px;padding:28px clamp(24px,4vw,48px) 48px;display:flex;overflow:auto}.DGiSta_page>*{width:100%;max-width:960px}.DGiSta_pageHead{justify-content:space-between;align-items:flex-start;gap:16px;display:flex}.DGiSta_pageTitle{margin:0;font-size:20px;font-weight:500;line-height:28px}.DGiSta_pageIntro{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:13px;line-height:20px}.DGiSta_toolbar{justify-content:flex-end;align-items:center;gap:16px;display:flex}.DGiSta_iconButton{width:28px;height:28px;color:var(--dsw-alias-label-caption);cursor:pointer;background:0 0;border:0;border-radius:28px;justify-content:center;align-items:center;display:inline-flex}.DGiSta_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.DGiSta_iconButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.DGiSta_iconWrap{display:inline-flex}.DGiSta_button{border:.5px solid var(--dsw-alias-border-l3);height:32px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:0 12px;font-size:13px}.DGiSta_button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.DGiSta_button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.DGiSta_search{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);width:100%;height:36px;color:var(--dsw-alias-label-primary);background:0 0;border-radius:10px;padding:0 12px;font-size:13px}.DGiSta_search::placeholder{color:var(--dsw-alias-label-tertiary)}.DGiSta_group{margin-top:32px}.DGiSta_groupTitle{align-items:baseline;gap:8px;margin:0 0 4px;font-size:15px;font-weight:500;line-height:22px;display:flex}.DGiSta_groupCount{color:var(--dsw-alias-label-tertiary);font-size:13px;font-weight:400}.DGiSta_groupHint{color:var(--dsw-alias-label-caption);font-size:12px;font-weight:400}.DGiSta_cards{margin:0;padding:0;list-style:none}.DGiSta_card{border-radius:12px;min-width:0;margin:0 -8px}.DGiSta_card:hover{background:var(--dsw-alias-interactive-bg-hover)}.DGiSta_cardHead{align-items:center;gap:14px;padding:8px;display:flex}.DGiSta_cardIcon{border:.5px solid var(--dsw-alias-border-l3);width:48px;height:48px;color:var(--dsw-alias-label-secondary);border-radius:10px;flex:none;justify-content:center;align-items:center;display:inline-flex}.DGiSta_cardForeign{opacity:.72}.DGiSta_cardMain{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.DGiSta_workspaceBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);vertical-align:middle;border-radius:4px;margin-left:8px;padding:1px 6px;font-size:11px;font-weight:400;line-height:16px}.DGiSta_cardTitle{text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:500;line-height:20px;overflow:hidden}.DGiSta_cardDesc{color:var(--dsw-alias-label-secondary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:13px;line-height:18px;display:-webkit-box;overflow:hidden}.DGiSta_cardWhenToUse{color:var(--dsw-alias-label-caption);font-size:12px;line-height:18px}.DGiSta_cardMarks{flex-wrap:wrap;gap:6px;margin-top:2px;display:flex}.DGiSta_mark{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-tertiary);border-radius:4px;padding:1px 6px;font-size:11px;line-height:16px}.DGiSta_markOn{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.DGiSta_markOff{border-color:var(--dsw-alias-border-l4);color:var(--dsw-alias-label-caption)}.DGiSta_markDanger{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.DGiSta_cardEnd{flex:none;align-items:center;gap:8px;display:flex}.DGiSta_switch{background:var(--dsw-alias-border-l3);cursor:pointer;border:none;border-radius:10px;width:36px;height:20px;transition:background .12s;position:relative}.DGiSta_switch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.DGiSta_switchOn{background:var(--dsw-alias-state-success-primary)}.DGiSta_switch:after{content:\"\";background:var(--dsw-alias-bg-layer-1);border-radius:50%;width:16px;height:16px;transition:transform .12s;position:absolute;top:2px;left:2px}.DGiSta_switchOn:after{transform:translate(16px)}.DGiSta_dialog{width:min(560px,100%)}.DGiSta_form{flex-direction:column;gap:12px;display:flex}.DGiSta_field{flex-direction:column;gap:4px;display:flex}.DGiSta_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}.DGiSta_input{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);height:32px;color:var(--dsw-alias-label-primary);background:0 0;border-radius:8px;padding:0 10px;font-size:13px}.DGiSta_textarea{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);min-height:80px;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-markdown-code-block-small);resize:vertical;background:0 0;border-radius:8px;padding:8px 10px;font-size:12px}.DGiSta_status{text-align:center;color:var(--dsw-alias-label-tertiary);padding:32px 0;font-size:13px}.DGiSta_statusError{color:var(--dsw-alias-state-error-primary)}";
12
12
  const tagId = "@sparkelf/dsh-client-ui-skill-center/SkillCenterPage.module.css";
13
13
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
14
14
  const tag = document.createElement("style");
@@ -22,6 +22,7 @@ window.__ModuleLoader__.load({
22
22
  "card": "DGiSta_card",
23
23
  "cardDesc": "DGiSta_cardDesc",
24
24
  "cardEnd": "DGiSta_cardEnd",
25
+ "cardForeign": "DGiSta_cardForeign",
25
26
  "cardHead": "DGiSta_cardHead",
26
27
  "cardIcon": "DGiSta_cardIcon",
27
28
  "cardMain": "DGiSta_cardMain",
@@ -54,10 +55,11 @@ window.__ModuleLoader__.load({
54
55
  "switch": "DGiSta_switch",
55
56
  "switchOn": "DGiSta_switchOn",
56
57
  "textarea": "DGiSta_textarea",
57
- "toolbar": "DGiSta_toolbar"
58
+ "toolbar": "DGiSta_toolbar",
59
+ "workspaceBadge": "DGiSta_workspaceBadge"
58
60
  };
59
61
  //#endregion
60
- //#region src/client/api.ts
62
+ //#region lib/types/client/api.js
61
63
  /**
62
64
  * Skill center API client. Talks to the host route family over same-origin
63
65
  * fetch; the host owns the trust fence.
@@ -129,7 +131,7 @@ window.__ModuleLoader__.load({
129
131
  }
130
132
  };
131
133
  //#endregion
132
- //#region src/client/SkillCenterPage.tsx
134
+ //#region lib/types/client/SkillCenterPage.js
133
135
  /**
134
136
  * Skill center page. Browses the loaded skills grouped by source, toggles model
135
137
  * invocation, creates a skill, and deletes one into a recoverable trash.
@@ -145,9 +147,9 @@ window.__ModuleLoader__.load({
145
147
  * @returns the labelled field.
146
148
  */
147
149
  function DraftField({ label, children }) {
148
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
150
+ return (0, react_jsx_runtime.jsxs)("div", {
149
151
  className: SkillCenterPage_module_css_default.field,
150
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
152
+ children: [(0, react_jsx_runtime.jsx)("span", {
151
153
  className: SkillCenterPage_module_css_default.fieldLabel,
152
154
  children: label
153
155
  }), children]
@@ -159,42 +161,45 @@ window.__ModuleLoader__.load({
159
161
  * @returns the row.
160
162
  */
161
163
  function SkillCard({ skill, t, onToggle, onDelete }) {
162
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
163
- className: SkillCenterPage_module_css_default.card,
164
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
164
+ return (0, react_jsx_runtime.jsx)("li", {
165
+ className: skill.isActiveWorkspace === false && skill.workspaceName !== void 0 ? SkillCenterPage_module_css_default.card + " " + SkillCenterPage_module_css_default.cardForeign : SkillCenterPage_module_css_default.card,
166
+ children: (0, react_jsx_runtime.jsxs)("div", {
165
167
  className: SkillCenterPage_module_css_default.cardHead,
166
168
  children: [
167
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
169
+ (0, react_jsx_runtime.jsx)("span", {
168
170
  className: SkillCenterPage_module_css_default.cardIcon,
169
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSkillOutline16, { size: 22 })
171
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSkillOutline16, { size: 22 })
170
172
  }),
171
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
173
+ (0, react_jsx_runtime.jsxs)("div", {
172
174
  className: SkillCenterPage_module_css_default.cardMain,
173
175
  children: [
174
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
176
+ (0, react_jsx_runtime.jsxs)("div", {
175
177
  className: SkillCenterPage_module_css_default.cardTitle,
176
- children: skill.name
178
+ children: [skill.name, skill.workspaceName === void 0 ? null : (0, react_jsx_runtime.jsx)("span", {
179
+ className: SkillCenterPage_module_css_default.workspaceBadge,
180
+ children: skill.workspaceName
181
+ })]
177
182
  }),
178
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
183
+ (0, react_jsx_runtime.jsx)("div", {
179
184
  className: SkillCenterPage_module_css_default.cardDesc,
180
185
  children: skill.description
181
186
  }),
182
- skill.whenToUse === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
187
+ skill.whenToUse === void 0 ? null : (0, react_jsx_runtime.jsx)("div", {
183
188
  className: SkillCenterPage_module_css_default.cardWhenToUse,
184
189
  children: skill.whenToUse
185
190
  }),
186
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
191
+ (0, react_jsx_runtime.jsxs)("div", {
187
192
  className: SkillCenterPage_module_css_default.cardMarks,
188
193
  children: [
189
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
194
+ (0, react_jsx_runtime.jsx)("span", {
190
195
  className: skill.modelInvocable ? SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markOn : SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markOff,
191
196
  children: t("skill.modelInvocable")
192
197
  }),
193
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
198
+ (0, react_jsx_runtime.jsx)("span", {
194
199
  className: skill.userInvocable ? SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markOn : SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markOff,
195
200
  children: t("skill.userInvocable")
196
201
  }),
197
- skill.linked === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
202
+ skill.linked === true ? (0, react_jsx_runtime.jsx)("span", {
198
203
  className: SkillCenterPage_module_css_default.mark + " " + SkillCenterPage_module_css_default.markDanger,
199
204
  children: t("skill.linked")
200
205
  }) : null
@@ -202,9 +207,9 @@ window.__ModuleLoader__.load({
202
207
  })
203
208
  ]
204
209
  }),
205
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
210
+ (0, react_jsx_runtime.jsxs)("div", {
206
211
  className: SkillCenterPage_module_css_default.cardEnd,
207
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
212
+ children: [(0, react_jsx_runtime.jsx)("button", {
208
213
  type: "button",
209
214
  className: skill.modelInvocable ? SkillCenterPage_module_css_default.switch + " " + SkillCenterPage_module_css_default.switchOn : SkillCenterPage_module_css_default.switch,
210
215
  "aria-label": skill.modelInvocable ? t("skill.disabled") : t("skill.enabled"),
@@ -212,7 +217,7 @@ window.__ModuleLoader__.load({
212
217
  onClick: () => {
213
218
  onToggle(skill, !skill.modelInvocable);
214
219
  }
215
- }), skill.path === void 0 || skill.linked === true ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
220
+ }), skill.path === void 0 || skill.linked === true ? null : (0, react_jsx_runtime.jsx)("button", {
216
221
  type: "button",
217
222
  className: SkillCenterPage_module_css_default.button,
218
223
  onClick: () => {
@@ -227,10 +232,10 @@ window.__ModuleLoader__.load({
227
232
  }
228
233
  /**
229
234
  * The skill center panel.
230
- * @param props - the slot's runtime share, providing the dictionary resolver.
235
+ * @param props - the slot's runtime share: the dictionary resolver and the workspace rows.
231
236
  * @returns the rendered panel.
232
237
  */
233
- function SkillCenterPage({ t }) {
238
+ function SkillCenterPage({ t, ...runtime }) {
234
239
  const api = (0, react.useMemo)(() => new SkillApi(), []);
235
240
  const [state, setState] = (0, react.useState)({ kind: "loading" });
236
241
  const [query, setQuery] = (0, react.useState)("");
@@ -242,11 +247,16 @@ window.__ModuleLoader__.load({
242
247
  content: "",
243
248
  root: "user"
244
249
  });
250
+ const useWorkspaces = runtime.useWorkspaces;
251
+ const workspaces = useWorkspaces === void 0 ? [] : useWorkspaces((snapshot) => snapshot.items);
252
+ const scope = (0, react.useMemo)(() => {
253
+ return (workspaces.find((item) => item.sessionIds.length > 0) ?? workspaces[0])?.path;
254
+ }, [workspaces]);
245
255
  const load = (0, react.useCallback)(async () => {
246
256
  try {
247
257
  setState({
248
258
  kind: "ready",
249
- payload: await api.list()
259
+ payload: await api.list(scope)
250
260
  });
251
261
  } catch (error) {
252
262
  setState({
@@ -254,7 +264,7 @@ window.__ModuleLoader__.load({
254
264
  message: error instanceof Error ? error.message : String(error)
255
265
  });
256
266
  }
257
- }, [api]);
267
+ }, [api, scope]);
258
268
  (0, react.useEffect)(() => {
259
269
  load();
260
270
  }, [load]);
@@ -334,20 +344,20 @@ window.__ModuleLoader__.load({
334
344
  load
335
345
  ]);
336
346
  const total = state.kind === "ready" ? state.payload.groups.reduce((n, g) => n + g.skills.length, 0) : 0;
337
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
347
+ return (0, react_jsx_runtime.jsxs)("div", {
338
348
  className: SkillCenterPage_module_css_default.page,
339
349
  children: [
340
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
350
+ (0, react_jsx_runtime.jsxs)("header", {
341
351
  className: SkillCenterPage_module_css_default.pageHead,
342
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
352
+ children: [(0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("h1", {
343
353
  className: SkillCenterPage_module_css_default.pageTitle,
344
354
  children: t("panel")
345
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
355
+ }), (0, react_jsx_runtime.jsx)("p", {
346
356
  className: SkillCenterPage_module_css_default.pageIntro,
347
357
  children: t("panel.intro")
348
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
358
+ })] }), (0, react_jsx_runtime.jsxs)("div", {
349
359
  className: SkillCenterPage_module_css_default.toolbar,
350
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
360
+ children: [(0, react_jsx_runtime.jsx)("button", {
351
361
  type: "button",
352
362
  className: SkillCenterPage_module_css_default.iconButton,
353
363
  "aria-label": t("action.refresh"),
@@ -355,15 +365,15 @@ window.__ModuleLoader__.load({
355
365
  onClick: () => {
356
366
  load();
357
367
  },
358
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
368
+ children: (0, react_jsx_runtime.jsx)("span", {
359
369
  className: SkillCenterPage_module_css_default.iconWrap,
360
370
  "aria-hidden": "true",
361
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline16, {})
371
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline16, {})
362
372
  })
363
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
373
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
364
374
  variant: "primary",
365
375
  size: "sm",
366
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 13 }),
376
+ icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 13 }),
367
377
  onClick: () => {
368
378
  setCreating((v) => !v);
369
379
  },
@@ -371,7 +381,7 @@ window.__ModuleLoader__.load({
371
381
  })]
372
382
  })]
373
383
  }),
374
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
384
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
375
385
  open: creating,
376
386
  onClose: () => {
377
387
  setCreating(false);
@@ -379,58 +389,58 @@ window.__ModuleLoader__.load({
379
389
  title: t("create.title"),
380
390
  closeLabel: t("action.cancel"),
381
391
  className: SkillCenterPage_module_css_default.dialog,
382
- footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
392
+ footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
383
393
  variant: "outline",
384
394
  autoFocus: true,
385
395
  onClick: () => {
386
396
  setCreating(false);
387
397
  },
388
398
  children: t("action.cancel")
389
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
399
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
390
400
  variant: "primary",
391
401
  onClick: () => {
392
402
  submit();
393
403
  },
394
404
  children: t("action.confirm")
395
405
  })] }),
396
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
406
+ children: (0, react_jsx_runtime.jsxs)("div", {
397
407
  className: SkillCenterPage_module_css_default.form,
398
408
  children: [
399
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftField, {
409
+ (0, react_jsx_runtime.jsx)(DraftField, {
400
410
  label: t("create.name"),
401
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
411
+ children: (0, react_jsx_runtime.jsx)("input", {
402
412
  className: SkillCenterPage_module_css_default.input,
403
413
  value: draft.name,
404
414
  onChange: set("name")
405
415
  })
406
416
  }),
407
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftField, {
417
+ (0, react_jsx_runtime.jsx)(DraftField, {
408
418
  label: t("create.description"),
409
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
419
+ children: (0, react_jsx_runtime.jsx)("input", {
410
420
  className: SkillCenterPage_module_css_default.input,
411
421
  value: draft.description,
412
422
  onChange: set("description")
413
423
  })
414
424
  }),
415
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftField, {
425
+ (0, react_jsx_runtime.jsx)(DraftField, {
416
426
  label: t("create.whenToUse"),
417
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
427
+ children: (0, react_jsx_runtime.jsx)("input", {
418
428
  className: SkillCenterPage_module_css_default.input,
419
429
  value: draft.whenToUse,
420
430
  onChange: set("whenToUse")
421
431
  })
422
432
  }),
423
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftField, {
433
+ (0, react_jsx_runtime.jsx)(DraftField, {
424
434
  label: t("create.content"),
425
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
435
+ children: (0, react_jsx_runtime.jsx)("textarea", {
426
436
  className: SkillCenterPage_module_css_default.textarea,
427
437
  value: draft.content,
428
438
  onChange: set("content")
429
439
  })
430
440
  }),
431
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DraftField, {
441
+ (0, react_jsx_runtime.jsx)(DraftField, {
432
442
  label: t("create.root"),
433
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
443
+ children: (0, react_jsx_runtime.jsxs)("select", {
434
444
  className: SkillCenterPage_module_css_default.input,
435
445
  value: draft.root,
436
446
  onChange: (e) => {
@@ -439,10 +449,10 @@ window.__ModuleLoader__.load({
439
449
  root: e.target.value === "project" ? "project" : "user"
440
450
  }));
441
451
  },
442
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
452
+ children: [(0, react_jsx_runtime.jsx)("option", {
443
453
  value: "user",
444
454
  children: t("create.rootUser")
445
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
455
+ }), (0, react_jsx_runtime.jsx)("option", {
446
456
  value: "project",
447
457
  children: t("create.rootProject")
448
458
  })]
@@ -451,7 +461,7 @@ window.__ModuleLoader__.load({
451
461
  ]
452
462
  })
453
463
  }),
454
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
464
+ (0, react_jsx_runtime.jsx)("input", {
455
465
  className: SkillCenterPage_module_css_default.search,
456
466
  placeholder: t("search.placeholder"),
457
467
  value: query,
@@ -459,11 +469,11 @@ window.__ModuleLoader__.load({
459
469
  setQuery(e.target.value);
460
470
  }
461
471
  }),
462
- state.kind === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
472
+ state.kind === "loading" ? (0, react_jsx_runtime.jsx)("div", {
463
473
  className: SkillCenterPage_module_css_default.status,
464
474
  children: t("state.loading")
465
475
  }) : null,
466
- state.kind === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
476
+ state.kind === "failed" ? (0, react_jsx_runtime.jsxs)("div", {
467
477
  className: SkillCenterPage_module_css_default.status + " " + SkillCenterPage_module_css_default.statusError,
468
478
  children: [
469
479
  t("state.failed"),
@@ -471,32 +481,32 @@ window.__ModuleLoader__.load({
471
481
  state.message
472
482
  ]
473
483
  }) : null,
474
- state.kind === "ready" && groups.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
484
+ state.kind === "ready" && groups.length === 0 ? (0, react_jsx_runtime.jsx)("div", {
475
485
  className: SkillCenterPage_module_css_default.status,
476
486
  children: query.trim() === "" ? t("state.empty") : t("search.noMatches")
477
487
  }) : null,
478
- state.kind === "ready" && groups.length > 0 && total === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
488
+ state.kind === "ready" && groups.length > 0 && total === 0 ? (0, react_jsx_runtime.jsx)("div", {
479
489
  className: SkillCenterPage_module_css_default.status,
480
490
  children: t("state.empty")
481
491
  }) : null,
482
- groups.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
492
+ groups.map((group) => (0, react_jsx_runtime.jsxs)("div", {
483
493
  className: SkillCenterPage_module_css_default.group,
484
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h2", {
494
+ children: [(0, react_jsx_runtime.jsxs)("h2", {
485
495
  className: SkillCenterPage_module_css_default.groupTitle,
486
496
  children: [
487
497
  group.title,
488
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
498
+ (0, react_jsx_runtime.jsx)("span", {
489
499
  className: SkillCenterPage_module_css_default.groupCount,
490
500
  children: group.skills.length
491
501
  }),
492
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
502
+ (0, react_jsx_runtime.jsx)("span", {
493
503
  className: SkillCenterPage_module_css_default.groupHint,
494
504
  children: group.hint
495
505
  })
496
506
  ]
497
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
507
+ }), (0, react_jsx_runtime.jsx)("ul", {
498
508
  className: SkillCenterPage_module_css_default.cards,
499
- children: group.skills.map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillCard, {
509
+ children: group.skills.map((skill) => (0, react_jsx_runtime.jsx)(SkillCard, {
500
510
  skill,
501
511
  t,
502
512
  onToggle: (s, enabled) => {
@@ -512,17 +522,23 @@ window.__ModuleLoader__.load({
512
522
  });
513
523
  }
514
524
  //#endregion
515
- //#region src/client/SkillCenterPanelIcon.tsx
525
+ //#region lib/types/client/SkillCenterPanelIcon.js
516
526
  /**
517
527
  * Render the glyph at the size the sidebar asks for.
518
528
  * @param props - the sidebar's icon share: requested edge and selected state.
519
529
  * @returns the icon element.
520
530
  */
521
531
  function SkillCenterPanelIcon({ size }) {
522
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSkillOutline16, { size });
532
+ return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSkillOutline16, { size });
523
533
  }
524
534
  //#endregion
525
- //#region src/client/locales.ts
535
+ //#region lib/types/client/locales.js
536
+ /**
537
+ * Skill center dictionaries. Keys are typed so the panel and the sidebar entry
538
+ * cannot reference a string that does not exist in both languages.
539
+ *
540
+ * @module @sparkelf/dsh-client-ui-skill-center/client/locales
541
+ */
526
542
  /** Simplified Chinese dictionary. */
527
543
  const zh = {
528
544
  "panel": "技能中心",
@@ -584,7 +600,7 @@ window.__ModuleLoader__.load({
584
600
  "delete.confirm": "Deleting moves it into .trash, where it can be restored. Delete?"
585
601
  };
586
602
  //#endregion
587
- //#region src/client/index.ts
603
+ //#region lib/types/client/index.js
588
604
  /** Dictionary namespace owned by this plugin. */
589
605
  const NS = "skillCenter";
590
606
  /** The id shared by the sidebar entry and the main panel it opens. */
package/lib/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { existsSync } from "node:fs";
2
- import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
1
+ import { existsSync, lstatSync } from "node:fs";
2
+ import { mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
- import { dirname, join, resolve } from "node:path";
4
+ import { basename, dirname, join, resolve } from "node:path";
5
5
  //#region lib/types/index.js
6
6
  /**
7
7
  * Skill center — host half. Serves the skill-center data source over the
@@ -40,37 +40,37 @@ const SOURCE_GROUPS = [
40
40
  {
41
41
  key: "bundled",
42
42
  title: "系统内置",
43
- hint: "随 Harness 一同安装,不能删除"
43
+ hint: "随 Harness 一同安装,对所有项目生效"
44
44
  },
45
45
  {
46
- key: "runtime",
47
- title: "运行时注册",
48
- hint: "由已安装的插件在运行时注册"
46
+ key: "project-dsh",
47
+ title: "项目 .dsh/skills",
48
+ hint: "位于项目目录,仅对该工作区生效"
49
49
  },
50
50
  {
51
- key: "user-agents",
52
- title: "用户 ~/.agents/skills",
53
- hint: "在用户 agents 目录下,对所有项目生效"
51
+ key: "project-agents",
52
+ title: "项目 .agents/skills",
53
+ hint: "位于项目目录,随项目提交,对协作者共享"
54
54
  },
55
55
  {
56
- key: "user-dsh",
57
- title: "用户 ~/.dsh/skills",
58
- hint: "在用户 dsh 目录下,对所有项目生效"
56
+ key: "custom",
57
+ title: "自定义目录",
58
+ hint: "由本插件的 customSkillDirs 配置"
59
59
  },
60
60
  {
61
- key: "project-agents",
62
- title: "项目 .agents/skills",
63
- hint: "随项目提交,对协作者共享"
61
+ key: "user-dsh",
62
+ title: "用户 ~/.dsh/skills",
63
+ hint: "在用户 dsh 目录下,对本机所有项目生效"
64
64
  },
65
65
  {
66
- key: "project-dsh",
67
- title: "项目 .dsh/skills",
68
- hint: "项目本地技能,通常不提交"
66
+ key: "user-agents",
67
+ title: "用户 ~/.agents/skills",
68
+ hint: "在用户 agents 目录下,对本机所有项目生效"
69
69
  },
70
70
  {
71
- key: "custom",
72
- title: "自定义目录",
73
- hint: "由本插件的 customSkillDirs 配置"
71
+ key: "runtime",
72
+ title: "运行时注册",
73
+ hint: "由已安装的插件在运行时注册"
74
74
  }
75
75
  ];
76
76
  /**
@@ -124,6 +124,117 @@ function levelOfSource(source, provider) {
124
124
  if (s.includes("runtime")) return "runtime";
125
125
  return "custom";
126
126
  }
127
+ /** The Markdown a skill directory may declare its bundle with. */
128
+ const SKILL_FILE = "SKILL.md";
129
+ /**
130
+ * Read one root directory for skills.
131
+ *
132
+ * The web-app bundle disables the host `skill-filesystem` row and lets each preset
133
+ * own local discovery, so the host registry carries only the bundled catalog: a
134
+ * panel reading the registry alone shows the shipped skills and none of the
135
+ * project or user ones the agent is actually running with. Reading the root
136
+ * conventions here closes that gap without a second catalog authority — the
137
+ * registry still owns discovery for anything it does serve, and this scan is
138
+ * merged under it by name.
139
+ *
140
+ * Both layouts the harness accepts are read: a directory holding SKILL.md, and a
141
+ * flat Markdown file.
142
+ *
143
+ * @param root - absolute root directory to scan.
144
+ * @param level - the group key every skill under this root belongs to.
145
+ * @returns the skills found, sorted by name; missing roots yield none.
146
+ */
147
+ async function scanSkillRoot(root, level, workspace) {
148
+ let entries;
149
+ try {
150
+ entries = await readdir(root, { withFileTypes: true });
151
+ } catch {
152
+ return [];
153
+ }
154
+ const found = [];
155
+ for (const entry of entries) {
156
+ const path = entry.isDirectory() || entry.isSymbolicLink() ? join(root, entry.name, SKILL_FILE) : entry.name.endsWith(".md") ? join(root, entry.name) : void 0;
157
+ if (path === void 0 || !existsSync(path)) continue;
158
+ const name = entry.name.replace(/\.md$/u, "");
159
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(name)) continue;
160
+ let source;
161
+ try {
162
+ source = await readFile(path, "utf8");
163
+ } catch {
164
+ continue;
165
+ }
166
+ const front = /^---\r?\n([\s\S]*?)\r?\n---/.exec(source)?.[1] ?? "";
167
+ const field = (key) => {
168
+ return new RegExp("^\\s*" + key + "\\s*:\\s*(.+?)\\s*$", "mu").exec(front)?.[1]?.replace(/^['"]|['"]$/gu, "");
169
+ };
170
+ const disabled = readDisabledFlag(source);
171
+ const invocation = field("invocation");
172
+ found.push({
173
+ name,
174
+ description: field("description") ?? "",
175
+ level,
176
+ path,
177
+ linked: lstatSyncSafe(path),
178
+ modelInvocable: disabled !== true && invocation !== "user",
179
+ userInvocable: invocation !== "model",
180
+ ...field("when-to-use") === void 0 ? {} : { whenToUse: field("when-to-use") },
181
+ ...workspace === void 0 ? {} : {
182
+ workspaceRoot: workspace.root,
183
+ workspaceName: workspace.name,
184
+ isActiveWorkspace: workspace.active
185
+ }
186
+ });
187
+ }
188
+ return found.sort((a, b) => a.name.localeCompare(b.name));
189
+ }
190
+ /** Whether a skill file is reached through a symlink, which makes deletion unsafe. */
191
+ function lstatSyncSafe(path) {
192
+ try {
193
+ return lstatSync(path).isSymbolicLink() || lstatSync(dirname(path)).isSymbolicLink();
194
+ } catch {
195
+ return false;
196
+ }
197
+ }
198
+ /**
199
+ * Every local skill root this deployment uses, with the group each belongs to.
200
+ *
201
+ * @param deps - the resolved configuration.
202
+ * @param projectRoots - project roots whose skill directories are in scope.
203
+ * @returns the roots to scan, in group order.
204
+ */
205
+ function localSkillRoots(deps, projectRoots, activeRoot) {
206
+ const roots = [];
207
+ for (const projectRoot of projectRoots) {
208
+ const workspace = {
209
+ root: projectRoot,
210
+ name: basename(projectRoot) || projectRoot,
211
+ active: projectRoot === activeRoot
212
+ };
213
+ roots.push({
214
+ path: join(projectRoot, ".dsh", "skills"),
215
+ level: "project-dsh",
216
+ workspace
217
+ });
218
+ roots.push({
219
+ path: join(projectRoot, ".agents", "skills"),
220
+ level: "project-agents",
221
+ workspace
222
+ });
223
+ }
224
+ for (const dir of deps.customSkillDirs) roots.push({
225
+ path: dir,
226
+ level: "custom"
227
+ });
228
+ roots.push({
229
+ path: join(deps.dshHome, "skills"),
230
+ level: "user-dsh"
231
+ });
232
+ roots.push({
233
+ path: join(deps.agentsHome, "skills"),
234
+ level: "user-agents"
235
+ });
236
+ return roots;
237
+ }
127
238
  /**
128
239
  * Read `disable-model-invocation` from a skill file.
129
240
  * @param source - the skill file's content.
@@ -196,11 +307,12 @@ async function readJson(req, maxBytes) {
196
307
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("request body must be a JSON object");
197
308
  return parsed;
198
309
  }
199
- /** The list handler: group the registry snapshot by source. */
310
+ /** The list handler: group the registry snapshot and the local roots together. */
200
311
  async function handleList(deps, cwd) {
201
312
  const projectRoots = [...new Set(deps.activeSessionCwds().map(findProjectRoot).concat(findProjectRoot(cwd)))];
202
313
  const snapshot = await deps.snapshotProject(cwd);
203
314
  const byGroup = new Map(SOURCE_GROUPS.map((g) => [g.key, []]));
315
+ const seen = /* @__PURE__ */ new Set();
204
316
  for (const skill of snapshot.skills) {
205
317
  const level = levelOfSource(skill.source, skill.provider);
206
318
  const entry = {
@@ -212,8 +324,26 @@ async function handleList(deps, cwd) {
212
324
  userInvocable: skill.invocation.userInvocable,
213
325
  ...skill.whenToUse === void 0 ? {} : { whenToUse: skill.whenToUse }
214
326
  };
327
+ seen.add(level + ":" + skill.name);
215
328
  (byGroup.get(level) ?? byGroup.get("custom"))?.push(entry);
216
329
  }
330
+ for (const root of localSkillRoots(deps, projectRoots, findProjectRoot(cwd))) for (const skill of await scanSkillRoot(root.path, root.level, root.workspace)) {
331
+ if (seen.has(root.level + ":" + skill.name)) continue;
332
+ seen.add(root.level + ":" + skill.name);
333
+ byGroup.get(root.level)?.push({
334
+ name: skill.name,
335
+ description: skill.description,
336
+ level: skill.level,
337
+ path: skill.path,
338
+ linked: skill.linked,
339
+ modelInvocable: skill.modelInvocable,
340
+ userInvocable: skill.userInvocable,
341
+ ...skill.whenToUse === void 0 ? {} : { whenToUse: skill.whenToUse },
342
+ ...skill.workspaceRoot === void 0 ? {} : { workspaceRoot: skill.workspaceRoot },
343
+ ...skill.workspaceName === void 0 ? {} : { workspaceName: skill.workspaceName },
344
+ ...skill.isActiveWorkspace === void 0 ? {} : { isActiveWorkspace: skill.isActiveWorkspace }
345
+ });
346
+ }
217
347
  const groups = [];
218
348
  for (const g of SOURCE_GROUPS) {
219
349
  const skills = (byGroup.get(g.key) ?? []).sort((a, b) => a.name.localeCompare(b.name));
@@ -393,4 +523,4 @@ function apply(ctx, config = {}) {
393
523
  for (const route of makeRoutes(deps)) ctx.effect(() => ctx.webServer.register(route), "ui-skill-center: route " + route.path);
394
524
  }
395
525
  //#endregion
396
- export { ROUTES, apply, buildSkillContent, findProjectRoot, inject, levelOfSource, makeRoutes, name, projectSkillRoot, readDisabledFlag, userSkillRoot, withDisabledFlag };
526
+ export { ROUTES, apply, buildSkillContent, findProjectRoot, inject, levelOfSource, localSkillRoots, makeRoutes, name, projectSkillRoot, readDisabledFlag, scanSkillRoot, userSkillRoot, withDisabledFlag };
@@ -16,8 +16,8 @@ export interface SkillCenterPageProps {
16
16
  }
17
17
  /**
18
18
  * The skill center panel.
19
- * @param props - the slot's runtime share, providing the dictionary resolver.
19
+ * @param props - the slot's runtime share: the dictionary resolver and the workspace rows.
20
20
  * @returns the rendered panel.
21
21
  */
22
- export declare function SkillCenterPage({ t }: PropsRuntime<'main'> & SkillCenterPageProps): ReactNode;
22
+ export declare function SkillCenterPage({ t, ...runtime }: PropsRuntime<'main'> & SkillCenterPageProps): ReactNode;
23
23
  //# sourceMappingURL=SkillCenterPage.d.ts.map
@@ -88,6 +88,62 @@ export declare function findProjectRoot(cwd: string): string;
88
88
  * @returns the group key.
89
89
  */
90
90
  export declare function levelOfSource(source: string, provider: string): string;
91
+ /** One skill found by reading a root directory. */
92
+ export interface ScannedSkill {
93
+ name: string;
94
+ description: string;
95
+ whenToUse?: string;
96
+ level: string;
97
+ path: string;
98
+ linked: boolean;
99
+ modelInvocable: boolean;
100
+ userInvocable: boolean;
101
+ /** The project a project-scoped skill came from; absent for user and bundled roots. */
102
+ workspaceRoot?: string;
103
+ workspaceName?: string;
104
+ isActiveWorkspace?: boolean;
105
+ }
106
+ /**
107
+ * Read one root directory for skills.
108
+ *
109
+ * The web-app bundle disables the host `skill-filesystem` row and lets each preset
110
+ * own local discovery, so the host registry carries only the bundled catalog: a
111
+ * panel reading the registry alone shows the shipped skills and none of the
112
+ * project or user ones the agent is actually running with. Reading the root
113
+ * conventions here closes that gap without a second catalog authority — the
114
+ * registry still owns discovery for anything it does serve, and this scan is
115
+ * merged under it by name.
116
+ *
117
+ * Both layouts the harness accepts are read: a directory holding SKILL.md, and a
118
+ * flat Markdown file.
119
+ *
120
+ * @param root - absolute root directory to scan.
121
+ * @param level - the group key every skill under this root belongs to.
122
+ * @returns the skills found, sorted by name; missing roots yield none.
123
+ */
124
+ export declare function scanSkillRoot(root: string, level: string, workspace?: {
125
+ root: string;
126
+ name: string;
127
+ active: boolean;
128
+ }): Promise<ScannedSkill[]>;
129
+ /**
130
+ * Every local skill root this deployment uses, with the group each belongs to.
131
+ *
132
+ * @param deps - the resolved configuration.
133
+ * @param projectRoots - project roots whose skill directories are in scope.
134
+ * @returns the roots to scan, in group order.
135
+ */
136
+ export declare function localSkillRoots(deps: Pick<SkillRoutesDeps, 'dshHome' | 'agentsHome'> & {
137
+ customSkillDirs: readonly string[];
138
+ }, projectRoots: readonly string[], activeRoot?: string): {
139
+ path: string;
140
+ level: string;
141
+ workspace?: {
142
+ root: string;
143
+ name: string;
144
+ active: boolean;
145
+ };
146
+ }[];
91
147
  /**
92
148
  * Read `disable-model-invocation` from a skill file.
93
149
  * @param source - the skill file's content.
package/package.json CHANGED
@@ -61,5 +61,5 @@
61
61
  },
62
62
  "type": "module",
63
63
  "types": "lib/types/index.d.ts",
64
- "version": "0.2.0-rc.14"
64
+ "version": "0.2.0-rc.16"
65
65
  }