@max-null/dsh-skill-mcp-center 0.1.0 → 0.3.1
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/client.js +379 -58
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -2
- package/dist/rpc.js +7 -0
- package/dist/service.d.ts +13 -1
- package/dist/service.js +35 -6
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -40,7 +40,17 @@ var CSS = `
|
|
|
40
40
|
.smc-tab:hover { color: var(--dsw-alias-label-secondary); }
|
|
41
41
|
.smc-tab.active { color: var(--dsw-alias-label-primary); border-bottom-color: var(--dsw-alias-state-business-primary); }
|
|
42
42
|
|
|
43
|
-
.smc-card { border: 1px solid var(--dsw-alias-border-l2); border-radius: 10px;
|
|
43
|
+
.smc-card { border: 1px solid var(--dsw-alias-border-l2); border-radius: 10px; margin-bottom: 10px; background: var(--dsw-alias-bg-layer-1); min-width: 0; overflow: hidden; }
|
|
44
|
+
.smc-card-pad { padding: 14px 16px; }
|
|
45
|
+
.smc-card:hover { background: var(--dsw-alias-interactive-bg-hover); }
|
|
46
|
+
.smc-card-head { display: flex; align-items: center; }
|
|
47
|
+
.smc-card-title { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; padding: 12px 8px 12px 16px; background: none; border: none; cursor: pointer; text-align: left; font-family: inherit; }
|
|
48
|
+
.smc-card-title:hover .smc-card-name { color: var(--dsw-alias-state-business-primary); }
|
|
49
|
+
.smc-card-name { font-size: 14px; font-weight: 600; color: var(--dsw-alias-label-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
50
|
+
.smc-card-desc { font-size: 12px; color: var(--dsw-alias-label-tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
51
|
+
.smc-chevron { flex: none; width: 8px; height: 8px; border-right: 1.5px solid var(--dsw-alias-label-caption); border-bottom: 1.5px solid var(--dsw-alias-label-caption); transform: rotate(45deg); transition: transform .15s; margin: 0 14px 0 4px; }
|
|
52
|
+
.smc-chevron.open { transform: rotate(-135deg); }
|
|
53
|
+
.smc-card .smc-detail { margin: 0; padding: 10px 16px 14px; border-top: 1px solid var(--dsw-alias-border-l1); }
|
|
44
54
|
.smc-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
45
55
|
.smc-name { font-weight: 600; color: var(--dsw-alias-label-primary); }
|
|
46
56
|
.smc-desc { color: var(--dsw-alias-label-secondary); font-size: 13px; margin-top: 4px; }
|
|
@@ -84,13 +94,50 @@ var CSS = `
|
|
|
84
94
|
.smc-toast.ok { border-color: var(--dsw-alias-state-success-primary); }
|
|
85
95
|
.smc-toast.error { border-color: var(--dsw-alias-state-error-primary); }
|
|
86
96
|
|
|
87
|
-
.smc-sidebar { display: flex; flex-direction: column; gap:
|
|
97
|
+
.smc-sidebar { display: flex; flex-direction: column; gap: 0; padding: 8px; height: 100%; box-sizing: border-box; }
|
|
98
|
+
.smc-sidebar-head { flex: none; display: flex; flex-direction: column; gap: 6px; padding-bottom: 8px; border-bottom: 1px solid var(--dsw-alias-border-l1); margin-bottom: 6px; }
|
|
99
|
+
.smc-sidebar-list { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
|
|
100
|
+
.smc-search-sidebar { height: 28px; padding: 0 12px; border-radius: 18px; border: 1px solid var(--dsw-alias-border-l2); background: var(--dsw-alias-bg-base); color: var(--dsw-alias-label-primary); font-size: 13px; outline: none; width: 100%; font-family: inherit; box-sizing: border-box; }
|
|
101
|
+
.smc-search-sidebar:focus { border-color: var(--dsw-alias-state-business-primary); }
|
|
102
|
+
.smc-search-sidebar::placeholder { color: var(--dsw-alias-label-caption); }
|
|
88
103
|
.smc-srv { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 8px; }
|
|
89
104
|
.smc-srv:hover { background: var(--dsw-alias-interactive-bg-hover); }
|
|
90
105
|
.smc-srv-name { font-size: 13px; font-weight: 500; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-primary); }
|
|
91
|
-
.smc-srv-
|
|
106
|
+
.smc-srv-desc { font-size: 11px; color: var(--dsw-alias-label-tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin: 0 10px 4px 26px; flex: none; }
|
|
92
107
|
.smc-srv-state { font-size: 11px; color: var(--dsw-alias-label-caption); padding: 0 10px 6px 26px; }
|
|
93
108
|
.smc-empty { padding: 24px; text-align: center; color: var(--dsw-alias-label-tertiary); font-size: 12.5px; }
|
|
109
|
+
.smc-ns-bar { display: flex; gap: 4px; margin-bottom: 8px; }
|
|
110
|
+
.smc-ns-btn { flex: 1; height: 26px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 8px; background: none; color: var(--dsw-alias-label-secondary); font-size: 12px; cursor: pointer; font-family: inherit; }
|
|
111
|
+
.smc-ns-btn:hover { background: var(--dsw-alias-interactive-bg-hover); }
|
|
112
|
+
.smc-ns-btn.active { color: var(--dsw-alias-state-business-primary); border-color: var(--dsw-alias-state-business-primary); }
|
|
113
|
+
.smc-md { margin: 6px 0 2px; padding: 8px 10px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; background: var(--dsw-alias-bg-base); font-size: 11px; line-height: 1.6; color: var(--dsw-alias-label-secondary); white-space: pre-wrap; word-break: break-all; max-height: 240px; overflow-y: auto; font-family: ui-monospace, 'Cascadia Code', Consolas, monospace; }
|
|
114
|
+
.smc-ref { display: inline; padding: 0 3px; border-radius: 4px; background: var(--dsw-alias-state-business-tertiary); color: var(--dsw-alias-state-business-primary); cursor: pointer; text-decoration: underline dotted; }
|
|
115
|
+
.smc-ref:hover { background: var(--dsw-alias-state-business-primary); color: var(--dsw-alias-bg-base); }
|
|
116
|
+
.smc-md-err { margin: 6px 0 2px; font-size: 11px; color: var(--dsw-alias-state-error-primary); }
|
|
117
|
+
.smc-detail .smc-md { margin-top: 8px; }
|
|
118
|
+
.smc-search { position: sticky; top: 0; z-index: 1; height: 30px; padding: 0 12px; border-radius: 18px; border: 1px solid var(--dsw-alias-border-l2); background: var(--dsw-alias-bg-base); color: var(--dsw-alias-label-primary); font-size: 13px; outline: none; width: 240px; font-family: inherit; }
|
|
119
|
+
.smc-search:focus { border-color: var(--dsw-alias-state-business-primary); }
|
|
120
|
+
.smc-search::placeholder { color: var(--dsw-alias-label-caption); }
|
|
121
|
+
.smc-toolbar { position: sticky; top: 0; z-index: 1; display: flex; align-items: center; gap: 10px; margin-bottom: 12px; padding: 6px 2px; background: var(--dsw-alias-bg-layer-2); }
|
|
122
|
+
.smc-toolbar .smc-search { position: static; width: 100%; flex: 1; margin: 0; }
|
|
123
|
+
.smc-toolbar-count { flex: none; font-size: 12px; color: var(--dsw-alias-label-tertiary); white-space: nowrap; }
|
|
124
|
+
.smc-group { display: flex; align-items: baseline; gap: 8px; margin: 14px 0 8px; font-size: 13px; font-weight: 600; color: var(--dsw-alias-label-secondary); }
|
|
125
|
+
.smc-group .smc-count { font-size: 11px; font-weight: 400; color: var(--dsw-alias-label-caption); }
|
|
126
|
+
.smc-detail { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--dsw-alias-border-l1); font-size: 12px; color: var(--dsw-alias-label-secondary); word-break: break-all; }
|
|
127
|
+
.smc-detail-row { display: flex; align-items: center; gap: 6px; margin-top: 4px; }
|
|
128
|
+
/* DSH 0.1.x \u8BBE\u7F6E\u5BFC\u822A\u65E0 icon \u5951\u7EA6\uFF08external section \u4E00\u5F8B\u9ED8\u8BA4\u9F7F\u8F6E\uFF09\u3002settings-nav-icon
|
|
129
|
+
\u6807\u8BB0\u672C\u63D2\u4EF6\u884C\u540E\uFF1A\u9690\u85CF\u58F3\u6E32\u67D3\u7684\u9F7F\u8F6E SVG\uFF0C\u7528 Lucide wrench \u7684 currentColor mask \u66FF\u6362\uFF0C
|
|
130
|
+
\u8DDF\u968F\u539F\u751F\u5BFC\u822A hover/active \u989C\u8272\u4E14\u4E0D\u6539\u53D8\u58F3\u7684 16px \u56FE\u6807\u8282\u594F\u3002 */
|
|
131
|
+
[data-dsh-skill-mcp-center-settings-nav] > svg:first-child { display: none; }
|
|
132
|
+
[data-dsh-skill-mcp-center-settings-nav]::before {
|
|
133
|
+
content: '';
|
|
134
|
+
flex: none;
|
|
135
|
+
width: 16px;
|
|
136
|
+
height: 16px;
|
|
137
|
+
background: currentColor;
|
|
138
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
139
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
140
|
+
}
|
|
94
141
|
`;
|
|
95
142
|
var cssInjected = false;
|
|
96
143
|
function injectCss() {
|
|
@@ -101,6 +148,30 @@ function injectCss() {
|
|
|
101
148
|
style.textContent = CSS;
|
|
102
149
|
document.head.append(style);
|
|
103
150
|
}
|
|
151
|
+
var SETTINGS_NAV_MARKER = "data-dsh-skill-mcp-center-settings-nav";
|
|
152
|
+
function registerSettingsNavIcon(label) {
|
|
153
|
+
let disposed = false;
|
|
154
|
+
const sync = () => {
|
|
155
|
+
if (disposed) return;
|
|
156
|
+
const currentLabel = label().trim();
|
|
157
|
+
const buttons = document.querySelectorAll('[role="dialog"] nav button');
|
|
158
|
+
for (const button of buttons) {
|
|
159
|
+
const matches = currentLabel.length > 0 && button.textContent?.trim() === currentLabel;
|
|
160
|
+
if (matches) button.setAttribute(SETTINGS_NAV_MARKER, "");
|
|
161
|
+
else button.removeAttribute(SETTINGS_NAV_MARKER);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
sync();
|
|
165
|
+
const observer = new MutationObserver(sync);
|
|
166
|
+
observer.observe(document.body, { childList: true, subtree: true, characterData: true });
|
|
167
|
+
return () => {
|
|
168
|
+
disposed = true;
|
|
169
|
+
observer.disconnect();
|
|
170
|
+
document.querySelectorAll(`[${SETTINGS_NAV_MARKER}]`).forEach((element) => {
|
|
171
|
+
element.removeAttribute(SETTINGS_NAV_MARKER);
|
|
172
|
+
});
|
|
173
|
+
};
|
|
174
|
+
}
|
|
104
175
|
var LOCALE_NS = "@max-null/dsh-skill-mcp-center";
|
|
105
176
|
var t = (key) => key;
|
|
106
177
|
var localeRevision = 0;
|
|
@@ -122,10 +193,6 @@ var zhDict = {
|
|
|
122
193
|
loading: "\u52A0\u8F7D\u4E2D\u2026",
|
|
123
194
|
loadFailed: "\u52A0\u8F7D\u5931\u8D25\uFF1A{e}",
|
|
124
195
|
noSkills: "\u672A\u53D1\u73B0\u4EFB\u4F55 skill\uFF08\u68C0\u67E5 ~/.dsh/skills \u4E0E\u9879\u76EE .agents/skills\uFF09",
|
|
125
|
-
sourceSystem: "\u7CFB\u7EDF",
|
|
126
|
-
sourceUser: "\u7528\u6237",
|
|
127
|
-
sourceWorkspace: "\u5DE5\u4F5C\u533A",
|
|
128
|
-
sourceRuntime: "\u8FD0\u884C\u65F6",
|
|
129
196
|
sectionSub: "\u7BA1\u7406\uFF08\u9759\u6001\u914D\u7F6E\uFF09\xB7 \u8FD0\u884C\u65F6\u72B6\u6001\u5728\u53F3\u4FA7\u8FB9\u680F",
|
|
130
197
|
modelDisabled: "\u6A21\u578B\u5DF2\u505C\u7528",
|
|
131
198
|
toggleModelVisible: "\u5207\u6362\u6A21\u578B\u53EF\u89C1",
|
|
@@ -158,16 +225,29 @@ var zhDict = {
|
|
|
158
225
|
noMcpServer: "\u672A\u914D\u7F6E MCP server",
|
|
159
226
|
connected: "\u5DF2\u8FDE\u63A5",
|
|
160
227
|
notSynced: "\u672A\u540C\u6B65",
|
|
161
|
-
failed: "failed"
|
|
228
|
+
failed: "failed",
|
|
229
|
+
searchSkills: "\u641C\u7D22 skill\uFF08\u540D\u79F0 / \u63CF\u8FF0\uFF09\u2026",
|
|
230
|
+
groupGlobal: "\u5168\u5C40",
|
|
231
|
+
groupWorkspace: "\u5DE5\u4F5C\u533A",
|
|
232
|
+
groupBundled: "\u5185\u7F6E\uFF08DSH \u5B98\u65B9\uFF09",
|
|
233
|
+
readOnlyBadge: "\u5185\u7F6E\u53EA\u8BFB",
|
|
234
|
+
noMatch: "\u6CA1\u6709\u5339\u914D\u7684 skill",
|
|
235
|
+
detailPath: "\u8DEF\u5F84",
|
|
236
|
+
detailProvider: "provider",
|
|
237
|
+
clickForDetail: "\u70B9\u51FB\u67E5\u770B\u8BE6\u60C5",
|
|
238
|
+
allNamespaces: "\u5168\u90E8",
|
|
239
|
+
nsGlobal: "\u5168\u5C40",
|
|
240
|
+
nsWorkspace: "\u5DE5\u4F5C\u533A",
|
|
241
|
+
noWorkspace: "\u672A\u9009\u62E9\u5DE5\u4F5C\u533A",
|
|
242
|
+
viewMd: "\u67E5\u770B SKILL.md",
|
|
243
|
+
hideMd: "\u6536\u8D77",
|
|
244
|
+
mdLoadFailed: "\u8BFB\u53D6\u5931\u8D25\uFF1A{e}",
|
|
245
|
+
refOpen: "\u6253\u5F00\u6587\u4EF6\u5F15\u7528"
|
|
162
246
|
};
|
|
163
247
|
var enDict = {
|
|
164
248
|
loading: "Loading\u2026",
|
|
165
249
|
loadFailed: "Failed to load: {e}",
|
|
166
250
|
noSkills: "No skills found (check ~/.dsh/skills and project .agents/skills)",
|
|
167
|
-
sourceSystem: "system",
|
|
168
|
-
sourceUser: "user",
|
|
169
|
-
sourceWorkspace: "workspace",
|
|
170
|
-
sourceRuntime: "runtime",
|
|
171
251
|
sectionSub: "Manage (static config) \xB7 runtime status lives in the sidebar",
|
|
172
252
|
modelDisabled: "Model disabled",
|
|
173
253
|
toggleModelVisible: "Toggle model visibility",
|
|
@@ -200,11 +280,76 @@ var enDict = {
|
|
|
200
280
|
noMcpServer: "No MCP server",
|
|
201
281
|
connected: "Connected",
|
|
202
282
|
notSynced: "Not synced",
|
|
203
|
-
failed: "failed"
|
|
283
|
+
failed: "failed",
|
|
284
|
+
searchSkills: "Search skills (name / description)\u2026",
|
|
285
|
+
groupGlobal: "Global",
|
|
286
|
+
groupWorkspace: "Workspace",
|
|
287
|
+
groupBundled: "Built-in (DSH official)",
|
|
288
|
+
readOnlyBadge: "built-in read-only",
|
|
289
|
+
noMatch: "No matching skills",
|
|
290
|
+
detailPath: "path",
|
|
291
|
+
detailProvider: "provider",
|
|
292
|
+
clickForDetail: "click for details",
|
|
293
|
+
allNamespaces: "All",
|
|
294
|
+
nsGlobal: "Global",
|
|
295
|
+
nsWorkspace: "Workspace",
|
|
296
|
+
noWorkspace: "No workspace selected",
|
|
297
|
+
viewMd: "View SKILL.md",
|
|
298
|
+
hideMd: "Hide",
|
|
299
|
+
mdLoadFailed: "Failed to read: {e}",
|
|
300
|
+
refOpen: "Open file reference"
|
|
204
301
|
};
|
|
205
302
|
var rpc = async () => {
|
|
206
303
|
throw new Error("skill-mcp-center: rpc not wired");
|
|
207
304
|
};
|
|
305
|
+
var openFileRef = null;
|
|
306
|
+
var FILE_REF_RE = /@("([^"]+)"|([^\s"@]+))/g;
|
|
307
|
+
function looksLikePath(path) {
|
|
308
|
+
if (path.includes("\\")) return true;
|
|
309
|
+
if (path.startsWith("./") || path.startsWith("../") || path.startsWith("/") || path.startsWith("~/")) return true;
|
|
310
|
+
if (path.includes("/")) {
|
|
311
|
+
return path.includes(".");
|
|
312
|
+
}
|
|
313
|
+
const m = /\.([A-Za-z0-9]{1,6})$/.exec(path);
|
|
314
|
+
if (m === null) return false;
|
|
315
|
+
const ext = m[1].toLowerCase();
|
|
316
|
+
const COMMON_EXTS = /* @__PURE__ */ new Set(["md", "txt", "ts", "tsx", "js", "mjs", "cjs", "json", "yml", "yaml", "toml", "png", "jpg", "jpeg", "gif", "webp", "svg", "ico", "css", "scss", "html", "py", "rs", "go", "java", "c", "h", "cpp", "sh", "ps1", "bat", "exe", "zip", "tar", "gz", "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "log", "env", "lock", "csv", "xml", "sql", "db", "wasm"]);
|
|
317
|
+
return COMMON_EXTS.has(ext);
|
|
318
|
+
}
|
|
319
|
+
function splitFileRefs(text) {
|
|
320
|
+
const parts = [];
|
|
321
|
+
let last = 0;
|
|
322
|
+
let m;
|
|
323
|
+
FILE_REF_RE.lastIndex = 0;
|
|
324
|
+
while ((m = FILE_REF_RE.exec(text)) !== null) {
|
|
325
|
+
if (m.index > last) parts.push({ kind: "text", text: text.slice(last, m.index) });
|
|
326
|
+
const path = m[2] ?? m[3];
|
|
327
|
+
if (m[2] !== void 0 || looksLikePath(path)) {
|
|
328
|
+
parts.push({ kind: "ref", ref: { raw: m[0], path } });
|
|
329
|
+
} else {
|
|
330
|
+
parts.push({ kind: "text", text: m[0] });
|
|
331
|
+
}
|
|
332
|
+
last = m.index + m[0].length;
|
|
333
|
+
}
|
|
334
|
+
if (last < text.length) parts.push({ kind: "text", text: text.slice(last) });
|
|
335
|
+
return parts;
|
|
336
|
+
}
|
|
337
|
+
function MdWithRefs({ text }) {
|
|
338
|
+
const parts = splitFileRefs(text);
|
|
339
|
+
if (parts.every((p) => p.kind === "text")) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: text });
|
|
340
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: parts.map((p, i) => p.kind === "text" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: p.text }, i) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
341
|
+
"span",
|
|
342
|
+
{
|
|
343
|
+
className: "smc-ref",
|
|
344
|
+
title: t("refOpen"),
|
|
345
|
+
onClick: () => {
|
|
346
|
+
openFileRef?.(p.ref.path, p.ref.path);
|
|
347
|
+
},
|
|
348
|
+
children: p.ref.raw
|
|
349
|
+
},
|
|
350
|
+
i
|
|
351
|
+
)) });
|
|
352
|
+
}
|
|
208
353
|
var toastState = null;
|
|
209
354
|
var toastListeners = /* @__PURE__ */ new Set();
|
|
210
355
|
function showToast(message, kind = "ok") {
|
|
@@ -232,19 +377,25 @@ function useToast() {
|
|
|
232
377
|
}, [t2]);
|
|
233
378
|
return t2;
|
|
234
379
|
}
|
|
235
|
-
var
|
|
236
|
-
|
|
237
|
-
"
|
|
238
|
-
"
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
380
|
+
var SKILL_GROUPS = [
|
|
381
|
+
{ key: "global", labelKey: "groupGlobal", sources: ["user-dsh", "user-agents"] },
|
|
382
|
+
{ key: "workspace", labelKey: "groupWorkspace", sources: ["project-dsh", "project-agents"] },
|
|
383
|
+
{ key: "bundled", labelKey: "groupBundled", sources: ["bundled"] }
|
|
384
|
+
];
|
|
385
|
+
function groupOf(source) {
|
|
386
|
+
const idx = SKILL_GROUPS.findIndex((g) => g.sources.includes(source));
|
|
387
|
+
return idx === -1 ? SKILL_GROUPS.length : idx;
|
|
388
|
+
}
|
|
243
389
|
function SkillView() {
|
|
244
390
|
useLocale();
|
|
245
391
|
const [items, setItems] = (0, import_react.useState)(null);
|
|
246
392
|
const [error, setError] = (0, import_react.useState)(null);
|
|
247
393
|
const [busy, setBusy] = (0, import_react.useState)(null);
|
|
394
|
+
const [query, setQuery] = (0, import_react.useState)("");
|
|
395
|
+
const [expanded, setExpanded] = (0, import_react.useState)(null);
|
|
396
|
+
const [mdPath, setMdPath] = (0, import_react.useState)(null);
|
|
397
|
+
const [mdText, setMdText] = (0, import_react.useState)(null);
|
|
398
|
+
const [mdError, setMdError] = (0, import_react.useState)(null);
|
|
248
399
|
const load = (0, import_react.useCallback)(() => {
|
|
249
400
|
void rpc("listSkills").then(
|
|
250
401
|
(v) => {
|
|
@@ -261,7 +412,6 @@ function SkillView() {
|
|
|
261
412
|
}, [load]);
|
|
262
413
|
if (error !== null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "smc-sub", children: t("loadFailed", { e: error }) });
|
|
263
414
|
if (items === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "smc-sub", children: t("loading") });
|
|
264
|
-
if (items.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "smc-sub", children: t("noSkills") });
|
|
265
415
|
const toggle = (s) => {
|
|
266
416
|
setBusy(s.path);
|
|
267
417
|
void rpc("toggleSkill", { path: s.path }).then(
|
|
@@ -276,32 +426,117 @@ function SkillView() {
|
|
|
276
426
|
}
|
|
277
427
|
);
|
|
278
428
|
};
|
|
279
|
-
|
|
280
|
-
|
|
429
|
+
const openMd = (s) => {
|
|
430
|
+
if (mdPath === s.path) {
|
|
431
|
+
setMdPath(null);
|
|
432
|
+
setMdText(null);
|
|
433
|
+
setMdError(null);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
setMdPath(s.path);
|
|
437
|
+
setMdText(null);
|
|
438
|
+
setMdError(null);
|
|
439
|
+
void rpc("readSkill", { path: s.path }).then(
|
|
440
|
+
(v) => {
|
|
441
|
+
setMdText(v);
|
|
442
|
+
},
|
|
443
|
+
(e) => {
|
|
444
|
+
setMdError(e instanceof Error ? e.message : String(e));
|
|
445
|
+
}
|
|
446
|
+
);
|
|
447
|
+
};
|
|
448
|
+
const q = query.trim().toLowerCase();
|
|
449
|
+
const visible = items.filter((s) => q === "" || s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q));
|
|
450
|
+
const buckets = SKILL_GROUPS.map(() => []);
|
|
451
|
+
const other = [];
|
|
452
|
+
for (const s of visible) {
|
|
453
|
+
const g = groupOf(s.source);
|
|
454
|
+
if (g < SKILL_GROUPS.length) buckets[g].push(s);
|
|
455
|
+
else other.push(s);
|
|
456
|
+
}
|
|
457
|
+
const renderCard = (s) => {
|
|
458
|
+
const open = expanded === s.path;
|
|
281
459
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-card", children: [
|
|
282
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-
|
|
283
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
460
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-card-head", children: [
|
|
461
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
462
|
+
"button",
|
|
463
|
+
{
|
|
464
|
+
type: "button",
|
|
465
|
+
className: "smc-card-title",
|
|
466
|
+
"aria-expanded": open,
|
|
467
|
+
title: t("clickForDetail"),
|
|
468
|
+
onClick: () => {
|
|
469
|
+
setExpanded(open ? null : s.path);
|
|
470
|
+
},
|
|
471
|
+
children: [
|
|
472
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smc-card-name", children: s.name }),
|
|
473
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smc-card-desc", children: s.description })
|
|
474
|
+
]
|
|
475
|
+
}
|
|
476
|
+
),
|
|
477
|
+
s.writable ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
288
478
|
"button",
|
|
289
479
|
{
|
|
290
480
|
type: "button",
|
|
291
|
-
className: `smc-toggle${s.
|
|
292
|
-
disabled:
|
|
293
|
-
title:
|
|
481
|
+
className: `smc-toggle${s.modelInvocable ? " on" : ""}`,
|
|
482
|
+
disabled: busy === s.path,
|
|
483
|
+
title: t("toggleModelVisible"),
|
|
294
484
|
onClick: () => {
|
|
295
485
|
toggle(s);
|
|
296
486
|
},
|
|
297
487
|
"aria-label": s.modelInvocable ? t("disable") : t("enable")
|
|
298
488
|
}
|
|
299
|
-
)
|
|
489
|
+
) : null,
|
|
490
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `smc-chevron${open ? " open" : ""}` })
|
|
300
491
|
] }),
|
|
301
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
492
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-detail", children: [
|
|
493
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-detail-row", children: [
|
|
494
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "smc-badge", children: [
|
|
495
|
+
t("detailProvider"),
|
|
496
|
+
" \xB7 ",
|
|
497
|
+
s.provider
|
|
498
|
+
] }),
|
|
499
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smc-badge", children: s.source }),
|
|
500
|
+
!s.modelInvocable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smc-badge disabled", children: t("modelDisabled") }),
|
|
501
|
+
!s.writable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smc-badge disabled", children: t("readOnlyBadge") }),
|
|
502
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smc-spacer" }),
|
|
503
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "smc-btn", onClick: () => {
|
|
504
|
+
openMd(s);
|
|
505
|
+
}, children: mdPath === s.path ? t("hideMd") : t("viewMd") })
|
|
506
|
+
] }),
|
|
507
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-detail-row", children: [
|
|
508
|
+
t("detailPath"),
|
|
509
|
+
"\uFF1A",
|
|
510
|
+
s.path
|
|
511
|
+
] }),
|
|
512
|
+
mdPath === s.path && (mdError !== null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smc-md-err", children: t("mdLoadFailed", { e: mdError }) }) : mdText === null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smc-md", children: t("loading") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smc-md", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MdWithRefs, { text: mdText }) }))
|
|
513
|
+
] })
|
|
514
|
+
] }, s.path);
|
|
515
|
+
};
|
|
516
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
517
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-toolbar", children: [
|
|
518
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "smc-search", placeholder: t("searchSkills"), value: query, onChange: (e) => {
|
|
519
|
+
setQuery(e.target.value);
|
|
520
|
+
} }),
|
|
521
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smc-toolbar-count", children: visible.length })
|
|
522
|
+
] }),
|
|
523
|
+
visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "smc-sub", children: t("noMatch") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
524
|
+
SKILL_GROUPS.map((g, i) => buckets[i].length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
525
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-group", children: [
|
|
526
|
+
t(g.labelKey),
|
|
527
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smc-count", children: buckets[i].length })
|
|
528
|
+
] }),
|
|
529
|
+
buckets[i].map(renderCard)
|
|
530
|
+
] }, g.key) : null),
|
|
531
|
+
other.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
532
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-group", children: [
|
|
533
|
+
other[0]?.source ?? "",
|
|
534
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smc-count", children: other.length })
|
|
535
|
+
] }),
|
|
536
|
+
other.map(renderCard)
|
|
537
|
+
] }, "other") : null
|
|
538
|
+
] })
|
|
539
|
+
] });
|
|
305
540
|
}
|
|
306
541
|
function McpView() {
|
|
307
542
|
useLocale();
|
|
@@ -361,7 +596,7 @@ function McpView() {
|
|
|
361
596
|
setEditing(null);
|
|
362
597
|
load();
|
|
363
598
|
} }),
|
|
364
|
-
items.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-card", children: [
|
|
599
|
+
items.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-card smc-card-pad", children: [
|
|
365
600
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-row", children: [
|
|
366
601
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smc-name", children: s.serverName }),
|
|
367
602
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smc-badge", children: s.transport }),
|
|
@@ -522,9 +757,19 @@ function Toast() {
|
|
|
522
757
|
if (toast === null) return null;
|
|
523
758
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `smc-toast ${toast.kind}`, children: toast.message });
|
|
524
759
|
}
|
|
760
|
+
function skillInNamespace(s, ns) {
|
|
761
|
+
if (ns === "all") return true;
|
|
762
|
+
if (ns === "global") return s.source === "user-dsh" || s.source === "user-agents";
|
|
763
|
+
return s.source === "project-dsh" || s.source === "project-agents";
|
|
764
|
+
}
|
|
525
765
|
function SidebarSkillTab({ visible, cwd }) {
|
|
526
766
|
useLocale();
|
|
527
767
|
const [items, setItems] = (0, import_react.useState)([]);
|
|
768
|
+
const [ns, setNs] = (0, import_react.useState)("all");
|
|
769
|
+
const [query, setQuery] = (0, import_react.useState)("");
|
|
770
|
+
const [mdPath, setMdPath] = (0, import_react.useState)(null);
|
|
771
|
+
const [mdText, setMdText] = (0, import_react.useState)(null);
|
|
772
|
+
const [mdError, setMdError] = (0, import_react.useState)(null);
|
|
528
773
|
const load = (0, import_react.useCallback)(() => {
|
|
529
774
|
void rpc("listSkills", { cwd }).then(
|
|
530
775
|
(v) => {
|
|
@@ -539,26 +784,96 @@ function SidebarSkillTab({ visible, cwd }) {
|
|
|
539
784
|
if (!visible) return;
|
|
540
785
|
load();
|
|
541
786
|
}, [visible, load]);
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
"aria-label": s.modelInvocable ? t("disable") : t("enable")
|
|
787
|
+
const openMd = (path) => {
|
|
788
|
+
if (mdPath === path) {
|
|
789
|
+
setMdPath(null);
|
|
790
|
+
setMdText(null);
|
|
791
|
+
setMdError(null);
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
setMdPath(path);
|
|
795
|
+
setMdText(null);
|
|
796
|
+
setMdError(null);
|
|
797
|
+
void rpc("readSkill", { path, cwd }).then(
|
|
798
|
+
(v) => {
|
|
799
|
+
setMdText(v);
|
|
800
|
+
},
|
|
801
|
+
(e) => {
|
|
802
|
+
setMdError(e instanceof Error ? e.message : String(e));
|
|
559
803
|
}
|
|
560
|
-
)
|
|
561
|
-
|
|
804
|
+
);
|
|
805
|
+
};
|
|
806
|
+
const q = query.trim().toLowerCase();
|
|
807
|
+
const visibleItems = items.filter((s) => skillInNamespace(s, ns) && (q === "" || s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q)));
|
|
808
|
+
const noWorkspace = ns === "workspace" && (cwd === void 0 || cwd === "");
|
|
809
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-sidebar", children: [
|
|
810
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-sidebar-head", children: [
|
|
811
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
812
|
+
"input",
|
|
813
|
+
{
|
|
814
|
+
className: "smc-search-sidebar",
|
|
815
|
+
placeholder: t("searchSkills"),
|
|
816
|
+
value: query,
|
|
817
|
+
onChange: (e) => {
|
|
818
|
+
setQuery(e.target.value);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
),
|
|
822
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smc-ns-bar", style: { marginBottom: 0 }, children: ["all", "global", "workspace"].map((k) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
823
|
+
"button",
|
|
824
|
+
{
|
|
825
|
+
type: "button",
|
|
826
|
+
className: `smc-ns-btn${ns === k ? " active" : ""}`,
|
|
827
|
+
onClick: () => {
|
|
828
|
+
setNs(k);
|
|
829
|
+
setMdPath(null);
|
|
830
|
+
setMdText(null);
|
|
831
|
+
setMdError(null);
|
|
832
|
+
},
|
|
833
|
+
children: t(k === "all" ? "allNamespaces" : k === "global" ? "nsGlobal" : "nsWorkspace")
|
|
834
|
+
},
|
|
835
|
+
k
|
|
836
|
+
)) })
|
|
837
|
+
] }),
|
|
838
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smc-sidebar-list", children: noWorkspace ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smc-empty", children: t("noWorkspace") }) : visibleItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smc-empty", children: q !== "" ? t("noMatch") : t("noSkills") }) : visibleItems.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
839
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "smc-srv", title: `${s.description}
|
|
840
|
+
${s.path}`, children: [
|
|
841
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `smc-dot${s.modelInvocable ? "" : " idle"}` }),
|
|
842
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smc-srv-name", children: s.name }),
|
|
843
|
+
!s.modelInvocable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smc-srv-count", children: t("modelDisabled") }),
|
|
844
|
+
!s.writable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "smc-srv-count", children: t("readOnlyBadge") }),
|
|
845
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
846
|
+
"button",
|
|
847
|
+
{
|
|
848
|
+
type: "button",
|
|
849
|
+
className: "smc-btn",
|
|
850
|
+
style: { height: 20, padding: "0 6px", fontSize: 11 },
|
|
851
|
+
onClick: () => {
|
|
852
|
+
openMd(s.path);
|
|
853
|
+
},
|
|
854
|
+
"aria-label": mdPath === s.path ? t("hideMd") : t("viewMd"),
|
|
855
|
+
children: mdPath === s.path ? t("hideMd") : "MD"
|
|
856
|
+
}
|
|
857
|
+
),
|
|
858
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
859
|
+
"button",
|
|
860
|
+
{
|
|
861
|
+
type: "button",
|
|
862
|
+
className: `smc-toggle${s.modelInvocable ? " on" : ""}`,
|
|
863
|
+
title: t("toggleModelVisible"),
|
|
864
|
+
onClick: () => {
|
|
865
|
+
void rpc("toggleSkill", { path: s.path }).then(() => {
|
|
866
|
+
load();
|
|
867
|
+
});
|
|
868
|
+
},
|
|
869
|
+
"aria-label": s.modelInvocable ? t("disable") : t("enable")
|
|
870
|
+
}
|
|
871
|
+
)
|
|
872
|
+
] }),
|
|
873
|
+
s.description !== "" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smc-srv-desc", children: s.description }),
|
|
874
|
+
mdPath === s.path && (mdError !== null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smc-md-err", children: t("mdLoadFailed", { e: mdError }) }) : mdText === null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smc-md", children: t("loading") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "smc-md", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MdWithRefs, { text: mdText }) }))
|
|
875
|
+
] }, s.path)) })
|
|
876
|
+
] });
|
|
562
877
|
}
|
|
563
878
|
var inject = ["slots", "connection", "locale"];
|
|
564
879
|
function apply(ctx) {
|
|
@@ -581,6 +896,7 @@ function apply(ctx) {
|
|
|
581
896
|
localeRevision += 1;
|
|
582
897
|
localeListeners.forEach((l) => l());
|
|
583
898
|
}));
|
|
899
|
+
ctx.effect(() => registerSettingsNavIcon(() => "Skill & MCP"));
|
|
584
900
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
585
901
|
name: "settings.section",
|
|
586
902
|
id: "skill-mcp-center",
|
|
@@ -590,6 +906,11 @@ function apply(ctx) {
|
|
|
590
906
|
ctx.inject(["betterSidebar"], (sidebarCtx) => {
|
|
591
907
|
const service = sidebarCtx.betterSidebar;
|
|
592
908
|
if (service === void 0) return;
|
|
909
|
+
if (typeof service.openFile === "function") {
|
|
910
|
+
openFileRef = (path, title) => {
|
|
911
|
+
service.openFile?.({}, path, title);
|
|
912
|
+
};
|
|
913
|
+
}
|
|
593
914
|
sidebarCtx.effect(() => service.registerTab({
|
|
594
915
|
id: "@max-null/dsh-skill-mcp-center:mcp",
|
|
595
916
|
title: () => "MCP",
|
package/dist/index.d.ts
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* package's `dsh.client` declaration.
|
|
5
5
|
*/
|
|
6
6
|
import type { Context } from '@deepseek-ai/cordis';
|
|
7
|
+
import type { SkillConfig } from './service.ts';
|
|
7
8
|
export { SkillMcpService } from './service.ts';
|
|
8
|
-
export type { McpConfig, McpServer, McpServerStatus, SkillView } from './service.ts';
|
|
9
|
+
export type { McpConfig, McpServer, McpServerStatus, SkillConfig, SkillView } from './service.ts';
|
|
9
10
|
export declare const name = "dsh-skill-mcp-center";
|
|
10
11
|
/** The engine declares its own loader/tools dependencies. */
|
|
11
12
|
export declare const inject: string[];
|
|
12
|
-
export declare function apply(ctx: Context): Promise<void>;
|
|
13
|
+
export declare function apply(ctx: Context, config?: SkillConfig): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ export { SkillMcpService } from "./service.js";
|
|
|
4
4
|
export const name = 'dsh-skill-mcp-center';
|
|
5
5
|
/** The engine declares its own loader/tools dependencies. */
|
|
6
6
|
export const inject = ['loader', 'tools'];
|
|
7
|
-
export async function apply(ctx) {
|
|
8
|
-
await ctx.plugin(SkillMcpService);
|
|
7
|
+
export async function apply(ctx, config = {}) {
|
|
8
|
+
await ctx.plugin(SkillMcpService, config);
|
|
9
9
|
await ctx.plugin(SkillMcpRpc);
|
|
10
10
|
}
|
package/dist/rpc.js
CHANGED
|
@@ -26,6 +26,13 @@ export class SkillMcpRpc extends Service {
|
|
|
26
26
|
return internal('toggleSkill: path is required');
|
|
27
27
|
return { ok: true, value: await ctx.skillMcp.toggleSkill(path) };
|
|
28
28
|
}
|
|
29
|
+
case 'readSkill': {
|
|
30
|
+
const path = p.path;
|
|
31
|
+
if (typeof path !== 'string' || path === '')
|
|
32
|
+
return internal('readSkill: path is required');
|
|
33
|
+
const cwd = p.cwd;
|
|
34
|
+
return { ok: true, value: await ctx.skillMcp.readSkill(path, typeof cwd === 'string' ? cwd : undefined) };
|
|
35
|
+
}
|
|
29
36
|
case 'listMcpServers':
|
|
30
37
|
return { ok: true, value: await ctx.skillMcp.listMcpServers() };
|
|
31
38
|
case 'createMcpServer':
|
package/dist/service.d.ts
CHANGED
|
@@ -26,6 +26,11 @@ export interface SkillView {
|
|
|
26
26
|
/** Absolute SKILL.md path (the toggle target; opaque to the client display). */
|
|
27
27
|
path: string;
|
|
28
28
|
}
|
|
29
|
+
/** Plugin configuration for the skill/MCP engine. */
|
|
30
|
+
export interface SkillConfig {
|
|
31
|
+
/** Additional read-only official/bundled skill roots (e.g. the harness repo's own `.agents/skills`). */
|
|
32
|
+
officialSkillDirs?: string[];
|
|
33
|
+
}
|
|
29
34
|
/** One MCP server as the Settings surface exposes it. */
|
|
30
35
|
export interface McpServer {
|
|
31
36
|
id: string;
|
|
@@ -59,11 +64,18 @@ export interface McpConfig {
|
|
|
59
64
|
}
|
|
60
65
|
export declare class SkillMcpService extends Service {
|
|
61
66
|
static inject: string[];
|
|
62
|
-
|
|
67
|
+
private readonly officialSkillDirs;
|
|
68
|
+
constructor(ctx: Context, config?: SkillConfig);
|
|
63
69
|
/** User-level skills plus, when a workspace is given, its project-level skills. */
|
|
64
70
|
listSkills(cwd?: string): Promise<SkillView[]>;
|
|
65
71
|
/** Flip one disk-backed skill's model invocation by rewriting its SKILL.md frontmatter. */
|
|
66
72
|
toggleSkill(path: string): Promise<SkillView>;
|
|
73
|
+
/**
|
|
74
|
+
* Read one skill's SKILL.md raw text for display. Paths must live under a
|
|
75
|
+
* known skill root — a plain path join against the same roots `listSkills`
|
|
76
|
+
* scans, so the RPC cannot be used to read arbitrary files.
|
|
77
|
+
*/
|
|
78
|
+
readSkill(path: string, cwd?: string): Promise<string>;
|
|
67
79
|
/** Every `mcp-client` loader entry as a server card. */
|
|
68
80
|
listMcpServers(): Promise<McpServer[]>;
|
|
69
81
|
/** Add one mcp-client entry — hot-connects (create → init) and persists. */
|
package/dist/service.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { Service } from '@deepseek-ai/cordis';
|
|
10
10
|
import { readdir, readFile, writeFile } from 'node:fs/promises';
|
|
11
11
|
import { homedir } from 'node:os';
|
|
12
|
-
import { join } from 'node:path';
|
|
12
|
+
import { join, sep } from 'node:path';
|
|
13
13
|
import { parseSkillFrontmatter, setDisableModelInvocation } from "./frontmatter.js";
|
|
14
14
|
/** Runtime mirror of cordis FiberState (a cross-package const enum). */
|
|
15
15
|
const FIBER_PHASE = {
|
|
@@ -57,7 +57,7 @@ function skillPathFor(root, name, isDirectory) {
|
|
|
57
57
|
return null;
|
|
58
58
|
}
|
|
59
59
|
/** Scan one root for SKILL.md entries and parse their frontmatter. */
|
|
60
|
-
async function scanSkillRoot(root, source) {
|
|
60
|
+
async function scanSkillRoot(root, source, writable = true, provider = 'filesystem') {
|
|
61
61
|
let entries;
|
|
62
62
|
try {
|
|
63
63
|
entries = await readdir(root, { withFileTypes: true });
|
|
@@ -84,10 +84,10 @@ async function scanSkillRoot(root, source) {
|
|
|
84
84
|
name: fm.name,
|
|
85
85
|
description: fm.description,
|
|
86
86
|
source,
|
|
87
|
-
provider
|
|
87
|
+
provider,
|
|
88
88
|
modelInvocable: fm.modelInvocable,
|
|
89
89
|
userInvocable: true,
|
|
90
|
-
writable
|
|
90
|
+
writable,
|
|
91
91
|
path: skillPath,
|
|
92
92
|
});
|
|
93
93
|
}
|
|
@@ -95,8 +95,10 @@ async function scanSkillRoot(root, source) {
|
|
|
95
95
|
}
|
|
96
96
|
export class SkillMcpService extends Service {
|
|
97
97
|
static inject = ['loader', 'tools'];
|
|
98
|
-
|
|
98
|
+
officialSkillDirs;
|
|
99
|
+
constructor(ctx, config = {}) {
|
|
99
100
|
super(ctx, 'skillMcp');
|
|
101
|
+
this.officialSkillDirs = config.officialSkillDirs ?? [];
|
|
100
102
|
}
|
|
101
103
|
/** User-level skills plus, when a workspace is given, its project-level skills. */
|
|
102
104
|
async listSkills(cwd) {
|
|
@@ -108,6 +110,9 @@ export class SkillMcpService extends Service {
|
|
|
108
110
|
skills.push(...await scanSkillRoot(join(cwd, '.agents', 'skills'), 'project-agents'));
|
|
109
111
|
skills.push(...await scanSkillRoot(join(cwd, '.dsh', 'skills'), 'project-dsh'));
|
|
110
112
|
}
|
|
113
|
+
for (const dir of this.officialSkillDirs) {
|
|
114
|
+
skills.push(...await scanSkillRoot(dir, 'bundled', false, 'dsh-official'));
|
|
115
|
+
}
|
|
111
116
|
return skills;
|
|
112
117
|
}
|
|
113
118
|
/** Flip one disk-backed skill's model invocation by rewriting its SKILL.md frontmatter. */
|
|
@@ -135,6 +140,30 @@ export class SkillMcpService extends Service {
|
|
|
135
140
|
path,
|
|
136
141
|
};
|
|
137
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Read one skill's SKILL.md raw text for display. Paths must live under a
|
|
145
|
+
* known skill root — a plain path join against the same roots `listSkills`
|
|
146
|
+
* scans, so the RPC cannot be used to read arbitrary files.
|
|
147
|
+
*/
|
|
148
|
+
async readSkill(path, cwd) {
|
|
149
|
+
const roots = [...SKILL_ROOTS.map(root => root.path)];
|
|
150
|
+
if (cwd !== undefined && cwd !== '') {
|
|
151
|
+
roots.push(join(cwd, '.agents', 'skills'), join(cwd, '.dsh', 'skills'));
|
|
152
|
+
}
|
|
153
|
+
roots.push(...this.officialSkillDirs);
|
|
154
|
+
// Segment-boundary prefix check: a sibling directory like `skills-notes`
|
|
155
|
+
// must not satisfy a `skills` root. join(root, '') normalizes to root.
|
|
156
|
+
if (!roots.some(root => path === root || path.startsWith(`${root}${sep}`)))
|
|
157
|
+
throw new Error('skill-not-found');
|
|
158
|
+
let text;
|
|
159
|
+
try {
|
|
160
|
+
text = await readFile(path, 'utf8');
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
throw new Error('skill-not-found');
|
|
164
|
+
}
|
|
165
|
+
return text;
|
|
166
|
+
}
|
|
138
167
|
/** Every `mcp-client` loader entry as a server card. */
|
|
139
168
|
async listMcpServers() {
|
|
140
169
|
const servers = [];
|
|
@@ -182,7 +211,7 @@ export class SkillMcpService extends Service {
|
|
|
182
211
|
/** Runtime status per server: upstream `mcpStatus` seam when present, else derived. */
|
|
183
212
|
async mcpStatus() {
|
|
184
213
|
const servers = await this.listMcpServers();
|
|
185
|
-
const seam = this.ctx.mcpStatus;
|
|
214
|
+
const seam = this.ctx.get('mcpStatus');
|
|
186
215
|
if (seam !== undefined) {
|
|
187
216
|
const byName = new Map(seam.list().map(s => [s.serverName, s]));
|
|
188
217
|
return servers.map(s => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@max-null/dsh-skill-mcp-center",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Skill & MCP management center for DeepSeek Harness — manage skills and MCP servers in Settings, live MCP status in the sidebar",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|