@michengai/dsh-skills-manager 0.1.38 → 0.1.40

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/CHANGELOG.md CHANGED
@@ -6,6 +6,12 @@ The five most recent published versions are listed below.
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 0.1.40 - 2026-09-05
10
+
11
+ - 修复禁用的外部同名技能遮蔽已启用 DSH 技能的问题,统一管理页与运行时的赢家选择。
12
+ - 冲突时为用户和项目级原生赢家提供作用域候选,保留禁用阻断、工作区隔离及来源文件不变性。
13
+ - 显式声明作用域注册表回归所需的开发依赖,确保 CI 干净安装可执行完整检查。
14
+
9
15
  ## 0.1.38 - 2026-09-03
10
16
 
11
17
  - Added Cursor as a read-only, locally toggleable Skill source at `%USERPROFILE%\.cursor\skills`, with `DSH_CURSOR_HOME` override and state-file migration.
@@ -6,6 +6,12 @@
6
6
 
7
7
  ## 未发布
8
8
 
9
+ ## 0.1.40 - 2026-09-05
10
+
11
+ - 修复禁用的外部同名技能遮蔽已启用 DSH 技能的问题,统一管理页与运行时的赢家选择。
12
+ - 冲突时为用户和项目级原生赢家提供作用域候选,保留禁用阻断、工作区隔离及来源文件不变性。
13
+ - 显式声明作用域注册表回归所需的开发依赖,确保 CI 干净安装可执行完整检查。
14
+
9
15
  ## 0.1.38 - 2026-09-03
10
16
 
11
17
  - 新增 Cursor 用户级只读技能来源,默认读取 `%USERPROFILE%\.cursor\skills`,支持 `DSH_CURSOR_HOME` 覆盖,并兼容迁移既有状态文件。
package/lib/core.js CHANGED
@@ -1382,52 +1382,35 @@ async function listProviderCandidates(options = {}) {
1382
1382
  const candidates = [];
1383
1383
  const user = userRoots();
1384
1384
  const userScans = await scanDeduplicatedUserRoots(user);
1385
- for (const root of user) {
1386
- const scanned = userScans.get(root.key);
1387
- for (const entry of scanned.entries) {
1388
- if (!entry.loadable) continue;
1389
- const policy = effectiveSkillPolicy(policyResult, root, entry);
1390
- const needsOverlay = root.key !== "dsh" || policy.override !== void 0 || !entry.invocationPolicyValid || policyResult.writable === false;
1391
- if (!needsOverlay) continue;
1392
- candidates.push({
1393
- name: entry.declaredName,
1394
- description: entry.description,
1395
- invocation: { modelInvocable: policy.modelInvocable, userInvocable: policy.userInvocable },
1396
- provider: "dsh-skills-manager-external",
1397
- source: root.key === "dsh" ? "user-dsh" : `agent-${root.key}`,
1398
- rank: root.key === "dsh" ? USER_DSH_POLICY_RANK : entry.providerRank ?? root.rank,
1399
- locator: { rootKey: root.key, entryName: entry.name, path: entry.docPath, realEntryPath: entry.realEntryPath, realDocPath: entry.realDocPath },
1400
- resourceBase: { kind: "directory", path: entry.kind === "bundle" ? entry.realEntryPath : root.path },
1401
- path: entry.docPath,
1402
- metadata: { dshSkillsManager: { root: root.key, readOnly: !root.mutable, sourceReadOnly: !root.mutable, policyOnly: root.key === "dsh" } }
1403
- });
1404
- }
1405
- }
1385
+ const items = user.flatMap((root) => userScans.get(root.key).entries.map((entry) => ({ root, entry })));
1406
1386
  const cwd = options && typeof options.cwd === "string" ? options.cwd : void 0;
1407
1387
  if (cwd) {
1408
1388
  const roots = await projectRoots([cwd]);
1409
1389
  for (const root of roots) {
1410
1390
  const scanned = await scanEntries(root.path);
1411
- for (const entry of scanned.entries) {
1412
- if (!entry.loadable) continue;
1413
- const policy = effectiveSkillPolicy(policyResult, root, entry);
1414
- const needsOverlay = policy.override !== void 0 || !entry.invocationPolicyValid || policyResult.writable === false;
1415
- if (!needsOverlay) continue;
1416
- candidates.push({
1417
- name: entry.declaredName,
1418
- description: entry.description,
1419
- invocation: { modelInvocable: policy.modelInvocable, userInvocable: policy.userInvocable },
1420
- provider: "dsh-skills-manager-external",
1421
- source: root.kind,
1422
- rank: root.rank - 1,
1423
- locator: { rootKey: root.key, entryName: entry.name, path: entry.docPath, realEntryPath: entry.realEntryPath, realDocPath: entry.realDocPath },
1424
- resourceBase: { kind: "directory", path: entry.kind === "bundle" ? entry.realEntryPath : root.path },
1425
- path: entry.docPath,
1426
- metadata: { dshSkillsManager: { root: root.key, readOnly: !root.mutable, sourceReadOnly: !root.mutable, policyOnly: true } }
1427
- });
1428
- }
1391
+ for (const entry of scanned.entries) items.push({ root, entry });
1429
1392
  }
1430
1393
  }
1394
+ for (const group of groupLoadableSkillsByName(items).values()) {
1395
+ const { root, entry } = group[0];
1396
+ const project = root.scope === "project";
1397
+ const policyOnly = project || root.key === "dsh";
1398
+ const policy = effectiveSkillPolicy(policyResult, root, entry);
1399
+ const needsOverlay = !policyOnly || group.length > 1 || policy.override !== void 0 || !entry.invocationPolicyValid || policyResult.writable === false;
1400
+ if (!needsOverlay) continue;
1401
+ candidates.push({
1402
+ name: entry.declaredName,
1403
+ description: entry.description,
1404
+ invocation: { modelInvocable: policy.modelInvocable, userInvocable: policy.userInvocable },
1405
+ provider: "dsh-skills-manager-external",
1406
+ source: project ? root.kind : root.key === "dsh" ? "user-dsh" : `agent-${root.key}`,
1407
+ rank: project ? root.rank - 1 : root.key === "dsh" ? USER_DSH_POLICY_RANK : entry.providerRank ?? root.rank,
1408
+ locator: { rootKey: root.key, entryName: entry.name, path: entry.docPath, realEntryPath: entry.realEntryPath, realDocPath: entry.realDocPath },
1409
+ resourceBase: { kind: "directory", path: entry.kind === "bundle" ? entry.realEntryPath : root.path },
1410
+ path: entry.docPath,
1411
+ metadata: { dshSkillsManager: { root: root.key, readOnly: !root.mutable, sourceReadOnly: !root.mutable, policyOnly } }
1412
+ });
1413
+ }
1431
1414
  return candidates;
1432
1415
  }
1433
1416
  async function getProviderSkill(candidate, options = {}) {
@@ -1464,18 +1447,24 @@ async function getProviderSkill(candidate, options = {}) {
1464
1447
  function canonicalSkillName(item) {
1465
1448
  return item.entry.declaredName || item.entry.name;
1466
1449
  }
1467
- function markWinners(items, options = {}) {
1468
- const winners = /* @__PURE__ */ new Map();
1450
+ function groupLoadableSkillsByName(items) {
1451
+ const groups = /* @__PURE__ */ new Map();
1469
1452
  for (const item of [...items].sort((a, b) => a.root.rank - b.root.rank)) {
1470
- const canonicalName = canonicalSkillName(item);
1471
1453
  if (!item.entry.loadable) continue;
1472
- if (!winners.has(canonicalName)) {
1473
- winners.set(canonicalName, item);
1474
- } else if (options.markShadowed !== false) {
1475
- item.view.shadowedBy = { root: winners.get(canonicalName).root.key, name: winners.get(canonicalName).entry.name };
1476
- }
1454
+ const name = canonicalSkillName(item);
1455
+ const group = groups.get(name) || [];
1456
+ group.push(item);
1457
+ groups.set(name, group);
1477
1458
  }
1478
- for (const [canonicalName, winner] of winners) {
1459
+ return groups;
1460
+ }
1461
+ function markWinners(items, options = {}) {
1462
+ const winners = /* @__PURE__ */ new Map();
1463
+ for (const [canonicalName, [winner, ...shadowed]] of groupLoadableSkillsByName(items)) {
1464
+ winners.set(canonicalName, winner);
1465
+ if (options.markShadowed !== false) {
1466
+ for (const item of shadowed) item.view.shadowedBy = { root: winner.root.key, name: winner.entry.name };
1467
+ }
1479
1468
  if (options.markWinner !== false) winner.view.winner = true;
1480
1469
  winner.view.enabled = winner.policy.enabled;
1481
1470
  winner.view.canonicalName = canonicalName;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michengai/dsh-skills-manager",
3
- "version": "0.1.38",
3
+ "version": "0.1.40",
4
4
  "description": "NPM-installable DSH Web plugin for safely loading and managing skills across DSH and common local Agents.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -64,11 +64,13 @@
64
64
  "react": "^18.2.0"
65
65
  },
66
66
  "devDependencies": {
67
+ "@deepseek-ai/cordis": "4.0.2",
67
68
  "@deepseek-ai/dsh-client-locale": "0.1.2-rc.1",
68
69
  "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1",
69
70
  "@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1",
70
71
  "@deepseek-ai/dsh-host-webserver": "0.1.2-rc.1",
71
72
  "@deepseek-ai/dsh-session": "0.1.2-rc.1",
73
+ "@deepseek-ai/dsh-scope": "0.1.2-rc.1",
72
74
  "@deepseek-ai/dsh-skill": "0.1.2-rc.1",
73
75
  "@deepseek-ai/dsh-tools": "0.1.2-rc.1",
74
76
  "@deepseek-ai/dsh-web-app": "0.1.2-rc.1",