@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 +85 -69
- package/lib/index.js +154 -24
- package/lib/types/client/SkillCenterPage.d.ts +2 -2
- package/lib/types/index.d.ts +56 -0
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
150
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
149
151
|
className: SkillCenterPage_module_css_default.field,
|
|
150
|
-
children: [
|
|
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
|
|
163
|
-
className: SkillCenterPage_module_css_default.card,
|
|
164
|
-
children:
|
|
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
|
-
|
|
169
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
168
170
|
className: SkillCenterPage_module_css_default.cardIcon,
|
|
169
|
-
children:
|
|
171
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSkillOutline16, { size: 22 })
|
|
170
172
|
}),
|
|
171
|
-
|
|
173
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
172
174
|
className: SkillCenterPage_module_css_default.cardMain,
|
|
173
175
|
children: [
|
|
174
|
-
|
|
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
|
-
|
|
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 :
|
|
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
|
-
|
|
191
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
187
192
|
className: SkillCenterPage_module_css_default.cardMarks,
|
|
188
193
|
children: [
|
|
189
|
-
|
|
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
|
-
|
|
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 ?
|
|
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
|
-
|
|
210
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
206
211
|
className: SkillCenterPage_module_css_default.cardEnd,
|
|
207
|
-
children: [
|
|
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 :
|
|
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
|
|
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
|
|
347
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
338
348
|
className: SkillCenterPage_module_css_default.page,
|
|
339
349
|
children: [
|
|
340
|
-
|
|
350
|
+
(0, react_jsx_runtime.jsxs)("header", {
|
|
341
351
|
className: SkillCenterPage_module_css_default.pageHead,
|
|
342
|
-
children: [
|
|
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
|
-
}),
|
|
355
|
+
}), (0, react_jsx_runtime.jsx)("p", {
|
|
346
356
|
className: SkillCenterPage_module_css_default.pageIntro,
|
|
347
357
|
children: t("panel.intro")
|
|
348
|
-
})] }),
|
|
358
|
+
})] }), (0, react_jsx_runtime.jsxs)("div", {
|
|
349
359
|
className: SkillCenterPage_module_css_default.toolbar,
|
|
350
|
-
children: [
|
|
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:
|
|
368
|
+
children: (0, react_jsx_runtime.jsx)("span", {
|
|
359
369
|
className: SkillCenterPage_module_css_default.iconWrap,
|
|
360
370
|
"aria-hidden": "true",
|
|
361
|
-
children:
|
|
371
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline16, {})
|
|
362
372
|
})
|
|
363
|
-
}),
|
|
373
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
364
374
|
variant: "primary",
|
|
365
375
|
size: "sm",
|
|
366
|
-
icon:
|
|
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
|
-
|
|
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:
|
|
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
|
-
}),
|
|
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:
|
|
406
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
397
407
|
className: SkillCenterPage_module_css_default.form,
|
|
398
408
|
children: [
|
|
399
|
-
|
|
409
|
+
(0, react_jsx_runtime.jsx)(DraftField, {
|
|
400
410
|
label: t("create.name"),
|
|
401
|
-
children:
|
|
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
|
-
|
|
417
|
+
(0, react_jsx_runtime.jsx)(DraftField, {
|
|
408
418
|
label: t("create.description"),
|
|
409
|
-
children:
|
|
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
|
-
|
|
425
|
+
(0, react_jsx_runtime.jsx)(DraftField, {
|
|
416
426
|
label: t("create.whenToUse"),
|
|
417
|
-
children:
|
|
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
|
-
|
|
433
|
+
(0, react_jsx_runtime.jsx)(DraftField, {
|
|
424
434
|
label: t("create.content"),
|
|
425
|
-
children:
|
|
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
|
-
|
|
441
|
+
(0, react_jsx_runtime.jsx)(DraftField, {
|
|
432
442
|
label: t("create.root"),
|
|
433
|
-
children:
|
|
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: [
|
|
452
|
+
children: [(0, react_jsx_runtime.jsx)("option", {
|
|
443
453
|
value: "user",
|
|
444
454
|
children: t("create.rootUser")
|
|
445
|
-
}),
|
|
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
|
-
|
|
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" ?
|
|
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" ?
|
|
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 ?
|
|
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 ?
|
|
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) =>
|
|
492
|
+
groups.map((group) => (0, react_jsx_runtime.jsxs)("div", {
|
|
483
493
|
className: SkillCenterPage_module_css_default.group,
|
|
484
|
-
children: [
|
|
494
|
+
children: [(0, react_jsx_runtime.jsxs)("h2", {
|
|
485
495
|
className: SkillCenterPage_module_css_default.groupTitle,
|
|
486
496
|
children: [
|
|
487
497
|
group.title,
|
|
488
|
-
|
|
498
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
489
499
|
className: SkillCenterPage_module_css_default.groupCount,
|
|
490
500
|
children: group.skills.length
|
|
491
501
|
}),
|
|
492
|
-
|
|
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
|
-
}),
|
|
507
|
+
}), (0, react_jsx_runtime.jsx)("ul", {
|
|
498
508
|
className: SkillCenterPage_module_css_default.cards,
|
|
499
|
-
children: group.skills.map((skill) =>
|
|
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
|
|
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
|
|
532
|
+
return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSkillOutline16, { size });
|
|
523
533
|
}
|
|
524
534
|
//#endregion
|
|
525
|
-
//#region
|
|
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
|
|
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: "
|
|
47
|
-
title: "
|
|
48
|
-
hint: "
|
|
46
|
+
key: "project-dsh",
|
|
47
|
+
title: "项目 .dsh/skills",
|
|
48
|
+
hint: "位于项目目录,仅对该工作区生效"
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
|
-
key: "
|
|
52
|
-
title: "
|
|
53
|
-
hint: "
|
|
51
|
+
key: "project-agents",
|
|
52
|
+
title: "项目 .agents/skills",
|
|
53
|
+
hint: "位于项目目录,随项目提交,对协作者共享"
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
|
-
key: "
|
|
57
|
-
title: "
|
|
58
|
-
hint: "
|
|
56
|
+
key: "custom",
|
|
57
|
+
title: "自定义目录",
|
|
58
|
+
hint: "由本插件的 customSkillDirs 配置"
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
|
-
key: "
|
|
62
|
-
title: "
|
|
63
|
-
hint: "
|
|
61
|
+
key: "user-dsh",
|
|
62
|
+
title: "用户 ~/.dsh/skills",
|
|
63
|
+
hint: "在用户 dsh 目录下,对本机所有项目生效"
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
|
-
key: "
|
|
67
|
-
title: "
|
|
68
|
-
hint: "
|
|
66
|
+
key: "user-agents",
|
|
67
|
+
title: "用户 ~/.agents/skills",
|
|
68
|
+
hint: "在用户 agents 目录下,对本机所有项目生效"
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
|
-
key: "
|
|
72
|
-
title: "
|
|
73
|
-
hint: "
|
|
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
|
|
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
|
|
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
|
package/lib/types/index.d.ts
CHANGED
|
@@ -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