@xxxyz/dsh-mcp-manager 2.2.0 → 2.2.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/lib/index.js +61 -4
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -115,14 +115,27 @@ export default {
|
|
|
115
115
|
for (const name of await readState()) {
|
|
116
116
|
if (overrideSkills.has(name))
|
|
117
117
|
continue;
|
|
118
|
-
|
|
118
|
+
// Migrate pre-fix state entries: user-level skills are file-driven now
|
|
119
|
+
// (the scoped layer ignores global overrides), so rewrite the SKILL.md
|
|
120
|
+
// frontmatter and drop the stale state entry instead of creating an
|
|
121
|
+
// override that cannot shadow them.
|
|
122
|
+
const userDef = await findUserSkill(name);
|
|
123
|
+
if (userDef) {
|
|
124
|
+
await editUserSkillInvocation(name, false);
|
|
125
|
+
changed = true;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const def = await ctx.skills.get(name);
|
|
119
129
|
if (!def)
|
|
120
130
|
continue;
|
|
121
131
|
overrideSkills.set(name, { ...def, provider: OVERRIDE_PROVIDER, invocation: { modelInvocable: false, userInvocable: false } });
|
|
122
132
|
changed = true;
|
|
123
133
|
}
|
|
124
|
-
if (changed
|
|
125
|
-
|
|
134
|
+
if (changed) {
|
|
135
|
+
await saveState([...overrideSkills.keys()]);
|
|
136
|
+
if (skillProviderControl)
|
|
137
|
+
skillProviderControl.invalidate();
|
|
138
|
+
}
|
|
126
139
|
}
|
|
127
140
|
;
|
|
128
141
|
ctx.on('skills/change', () => { void ensureRestored(); });
|
|
@@ -275,6 +288,38 @@ export default {
|
|
|
275
288
|
const skills = snap.skills.concat(extra).sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
276
289
|
return { ok: true, skills, complete: snap.complete !== false };
|
|
277
290
|
}
|
|
291
|
+
// Toggle a USER-LEVEL filesystem skill by editing its SKILL.md frontmatter.
|
|
292
|
+
// This is the only mechanism that works: the rank-0 override provider lives
|
|
293
|
+
// in the global layer, but ctx.skills reads merge the viewing scope's chain
|
|
294
|
+
// and the scoped layer's filesystem entry WINS the duplicate name outright
|
|
295
|
+
// (collectFresh does `merged.set(name, entry)` per layer, last wins) — so an
|
|
296
|
+
// override can never hide a user skill from the / menu. Editing the source
|
|
297
|
+
// file makes the real dsh-skill-filesystem provider (which hot-reloads via
|
|
298
|
+
// its watcher) return the disabled invocation on every scope.
|
|
299
|
+
async function editUserSkillInvocation(name, enabled) {
|
|
300
|
+
const def = await findUserSkill(name);
|
|
301
|
+
if (!def || !def.path)
|
|
302
|
+
return { ok: false, error: '技能不存在: ' + name };
|
|
303
|
+
const raw = await fs.readText(await fs.resolve(def.path)).catch(() => '');
|
|
304
|
+
if (!raw)
|
|
305
|
+
return { ok: false, error: '无法读取技能文件: ' + def.path };
|
|
306
|
+
// Strip any existing invocation flags from the frontmatter block, then
|
|
307
|
+
// re-append the ones matching the requested state. Everything else —
|
|
308
|
+
// body content and other frontmatter keys — is preserved verbatim.
|
|
309
|
+
const fmMatch = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
|
|
310
|
+
if (!fmMatch)
|
|
311
|
+
return { ok: false, error: '技能文件缺少 frontmatter: ' + def.path };
|
|
312
|
+
const fmLines = fmMatch[1].split(/\r?\n/);
|
|
313
|
+
const rest = raw.slice(fmMatch[0].length);
|
|
314
|
+
const stripped = fmLines.filter((line) => !/^(user-invocable|disable-model-invocation):/i.test(line.trim()));
|
|
315
|
+
if (!enabled) {
|
|
316
|
+
stripped.push('user-invocable: false');
|
|
317
|
+
stripped.push('disable-model-invocation: true');
|
|
318
|
+
}
|
|
319
|
+
const next = '---\n' + stripped.join('\n') + '\n---' + rest;
|
|
320
|
+
await fs.writeText(await fs.resolve(def.path), next);
|
|
321
|
+
return { ok: true };
|
|
322
|
+
}
|
|
278
323
|
async function skillToggle(args) {
|
|
279
324
|
const name = String(args.name || '').trim();
|
|
280
325
|
if (!name)
|
|
@@ -284,6 +329,18 @@ export default {
|
|
|
284
329
|
await ensureRestored();
|
|
285
330
|
const enabled = args.enabled !== false;
|
|
286
331
|
return withWriteLock(async () => {
|
|
332
|
+
// User-level skills bypass the override provider entirely — editing the
|
|
333
|
+
// SKILL.md frontmatter is what makes the real provider (and thus the
|
|
334
|
+
// / menu) see the change.
|
|
335
|
+
const userDef = await findUserSkill(name);
|
|
336
|
+
if (userDef) {
|
|
337
|
+
// Also drop a stale override row if one was created pre-fix.
|
|
338
|
+
if (overrideSkills.delete(name)) {
|
|
339
|
+
await saveState([...overrideSkills.keys()]);
|
|
340
|
+
skillProviderControl?.invalidate();
|
|
341
|
+
}
|
|
342
|
+
return editUserSkillInvocation(name, enabled);
|
|
343
|
+
}
|
|
287
344
|
if (enabled) {
|
|
288
345
|
if (overrideSkills.delete(name)) {
|
|
289
346
|
await saveState([...overrideSkills.keys()]);
|
|
@@ -292,7 +349,7 @@ export default {
|
|
|
292
349
|
}
|
|
293
350
|
else {
|
|
294
351
|
if (!overrideSkills.has(name)) {
|
|
295
|
-
const def =
|
|
352
|
+
const def = await ctx.skills.get(name);
|
|
296
353
|
if (!def)
|
|
297
354
|
return { ok: false, error: '技能不存在: ' + name };
|
|
298
355
|
overrideSkills.set(name, { ...def, provider: OVERRIDE_PROVIDER, invocation: { modelInvocable: false, userInvocable: false } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xxxyz/dsh-mcp-manager",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "DSH-standard MCP manager plugin: Settings UI + HTTP API + model-facing mcp_manager_* tools. Install with one command: dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|