@michengai/dsh-skills-manager 0.1.37 → 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,11 +6,19 @@ 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
+
15
+ ## 0.1.38 - 2026-09-03
16
+
17
+ - Added Cursor as a read-only, locally toggleable Skill source at `%USERPROFILE%\.cursor\skills`, with `DSH_CURSOR_HOME` override and state-file migration.
18
+
9
19
  ## 0.1.37 - 2026-09-03
10
20
 
11
- - Updated the direct DSH development dependencies from `0.1.2-alpha.5` to `0.1.2-rc.1` without changing the published peer-dependency contract.
12
- - Rebased the dependency-version regression assertion on RC.1 and verified the complete package gate.
13
- - Verified plugin registration, the Skill provider, SessionStore access, and the HTTP state endpoint against the real RC.1 Cordis host services.
21
+ - Added compatibility with DeepSeek Harness `0.1.2-rc.1`.
14
22
 
15
23
  ## 0.1.36 - 2026-09-03
16
24
 
@@ -6,11 +6,19 @@
6
6
 
7
7
  ## 未发布
8
8
 
9
+ ## 0.1.40 - 2026-09-05
10
+
11
+ - 修复禁用的外部同名技能遮蔽已启用 DSH 技能的问题,统一管理页与运行时的赢家选择。
12
+ - 冲突时为用户和项目级原生赢家提供作用域候选,保留禁用阻断、工作区隔离及来源文件不变性。
13
+ - 显式声明作用域注册表回归所需的开发依赖,确保 CI 干净安装可执行完整检查。
14
+
15
+ ## 0.1.38 - 2026-09-03
16
+
17
+ - 新增 Cursor 用户级只读技能来源,默认读取 `%USERPROFILE%\.cursor\skills`,支持 `DSH_CURSOR_HOME` 覆盖,并兼容迁移既有状态文件。
18
+
9
19
  ## 0.1.37 - 2026-09-03
10
20
 
11
- - 将直接 DSH 开发依赖从 `0.1.2-alpha.5` 升级至 `0.1.2-rc.1`,不改动已发布的 peer 依赖兼容范围。
12
- - 将依赖版本回归断言同步到 RC.1,并完成完整发布门禁验证。
13
- - 使用真实 RC.1 Cordis 宿主服务验证插件注册、Skill Provider、SessionStore 访问和 HTTP 状态接口。
21
+ - 支持 DeepSeek Harness `0.1.2-rc.1`。
14
22
 
15
23
  ## 0.1.36 - 2026-09-03
16
24
 
package/README.md CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  ## Features
24
24
 
25
- - Discover and load user-level skills from `.agents`, CC Switch, Codex, Claude, Gemini, and OpenCode into DSH.
25
+ - Discover and load user-level skills from `.agents`, CC Switch, Codex, Claude, Gemini, OpenCode, and Cursor into DSH.
26
26
  - Discover project-level `.dsh/skills` and `.agents/skills` from active Session workspaces, grouped by project; every valid Skill can be enabled or disabled through non-mutating local policy.
27
27
  - Persist every per-Skill toggle under `$DSH_HOME/skills-manager/state.json` without rewriting any source Skill file.
28
28
  - Inspect Markdown bodies, frontmatter, invocation state, duplicate shadowing, and format diagnostics in a source-first UI.
@@ -153,6 +153,7 @@ Escape closes only the frontmost upload or confirmation dialog and leaves Settin
153
153
  | `$DSH_HOME\skills` | Yes | Manager state only | Yes | Moves to Trash |
154
154
  | `$DSH_AGENTS_HOME\skills` | Yes | Manager state only | No | No |
155
155
  | `~\.cc-switch\skills` | Yes, enabled by default | Manager state only | No | No |
156
+ | `%USERPROFILE%\.cursor\skills` (or `$DSH_CURSOR_HOME\skills`) | Yes | Manager state only | No | No |
156
157
  | `~/.codex/skills`, `~/.claude/skills`, `~/.gemini/skills`, `~/.config/opencode/skills` | Yes | Manager state only | No | No |
157
158
  | `<project>/.dsh/skills` | Yes, for active Session workspaces | Manager state only | Create in Settings | Moves to Trash and restores to the original project |
158
159
  | `<project>/.agents/skills` | Yes, for active Session workspaces | Manager state only | No | No |
package/README.zh-CN.md CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  ## 功能概览
24
24
 
25
- - 自动发现并把 `.agents`、CC Switch、Codex、Claude、Gemini 和 OpenCode 的用户级技能真正加载进 DSH。
25
+ - 自动发现并把 `.agents`、CC Switch、Codex、Claude、Gemini、OpenCode 和 Cursor 的用户级技能真正加载进 DSH。
26
26
  - 从活动 Session 工作区发现项目级 `.dsh/skills` 与 `.agents/skills` 并按项目分组;所有有效 Skill 都可通过不改源文件的本地策略启停。
27
27
  - 所有逐 Skill 启停都只写入 `$DSH_HOME\skills-manager\state.json`,绝不改写任何来源 Skill 文件。
28
28
  - 按来源折叠、搜索和筛选,并查看技能正文、frontmatter、调用状态、重名遮蔽与格式诊断。
@@ -153,6 +153,7 @@ dsh --profile web --dump-config
153
153
  | `$DSH_HOME\skills` | 支持 | 仅写 manager 状态 | 支持 | 进入回收站 |
154
154
  | `$DSH_AGENTS_HOME\skills` | 支持 | 仅写 manager 状态 | 不支持 | 不支持 |
155
155
  | `~\.cc-switch\skills` | 支持,默认开启 | 仅写 manager 状态 | 不支持 | 不支持 |
156
+ | `%USERPROFILE%\.cursor\skills`(或 `$DSH_CURSOR_HOME\skills`) | 支持 | 仅写 manager 状态 | 不支持 | 不支持 |
156
157
  | `~/.codex/skills`、`~/.claude/skills`、`~/.gemini/skills`、`~/.config/opencode/skills` | 支持 | 仅写 manager 状态 | 不支持 | 不支持 |
157
158
  | `<project>/.dsh/skills` | 支持活动 Session 工作区 | 仅写 manager 状态 | 设置页支持创建 | 进入回收站并恢复到原项目 |
158
159
  | `<project>/.agents/skills` | 支持活动 Session 工作区 | 仅写 manager 状态 | 不支持 | 不支持 |
package/lib/client.js CHANGED
@@ -186,7 +186,8 @@
186
186
  "root.codex": "Codex",
187
187
  "root.claude": "Claude",
188
188
  "root.gemini": "Gemini",
189
- "root.opencode": "OpenCode"
189
+ "root.opencode": "OpenCode",
190
+ "root.cursor": "Cursor"
190
191
  },
191
192
  en: {
192
193
  "title": "Skills",
@@ -364,7 +365,8 @@
364
365
  "root.codex": "Codex",
365
366
  "root.claude": "Claude",
366
367
  "root.gemini": "Gemini",
367
- "root.opencode": "OpenCode"
368
+ "root.opencode": "OpenCode",
369
+ "root.cursor": "Cursor"
368
370
  }
369
371
  };
370
372
  var CSS = `
package/lib/core.js CHANGED
@@ -41,7 +41,8 @@ function userRoots() {
41
41
  { key: "codex", path: join(process.env.DSH_CODEX_HOME || join(homedir(), ".codex"), "skills"), label: "Codex", mutable: false, toggleable: true, native: false, rank: 520 },
42
42
  { key: "claude", path: join(process.env.DSH_CLAUDE_HOME || join(homedir(), ".claude"), "skills"), label: "Claude", mutable: false, toggleable: true, native: false, rank: 530 },
43
43
  { key: "gemini", path: join(process.env.DSH_GEMINI_HOME || join(homedir(), ".gemini"), "skills"), label: "Gemini", mutable: false, toggleable: true, native: false, rank: 540 },
44
- { key: "opencode", path: join(process.env.DSH_OPENCODE_HOME || join(homedir(), ".config", "opencode"), "skills"), label: "OpenCode", mutable: false, toggleable: true, native: false, rank: 550 }
44
+ { key: "opencode", path: join(process.env.DSH_OPENCODE_HOME || join(homedir(), ".config", "opencode"), "skills"), label: "OpenCode", mutable: false, toggleable: true, native: false, rank: 550 },
45
+ { key: "cursor", path: join(process.env.DSH_CURSOR_HOME || join(homedir(), ".cursor"), "skills"), label: "Cursor", mutable: false, toggleable: true, native: false, rank: 560 }
45
46
  ];
46
47
  }
47
48
  function projectIdentity(path) {
@@ -647,6 +648,9 @@ function migrateManagerStateDocument(value) {
647
648
  if (value.sources && typeof value.sources === "object" && !Array.isArray(value.sources) && value.sources.ccswitch === void 0) value.sources.ccswitch = true;
648
649
  if (value.disabledSkills && typeof value.disabledSkills === "object" && !Array.isArray(value.disabledSkills) && value.disabledSkills.ccswitch === void 0) value.disabledSkills.ccswitch = [];
649
650
  if (value.enabledSkills && typeof value.enabledSkills === "object" && !Array.isArray(value.enabledSkills) && value.enabledSkills.ccswitch === void 0) value.enabledSkills.ccswitch = [];
651
+ if (value.sources && typeof value.sources === "object" && !Array.isArray(value.sources) && value.sources.cursor === void 0) value.sources.cursor = true;
652
+ if (value.disabledSkills && typeof value.disabledSkills === "object" && !Array.isArray(value.disabledSkills) && value.disabledSkills.cursor === void 0) value.disabledSkills.cursor = [];
653
+ if (value.enabledSkills && typeof value.enabledSkills === "object" && !Array.isArray(value.enabledSkills) && value.enabledSkills.cursor === void 0) value.enabledSkills.cursor = [];
650
654
  return value;
651
655
  }
652
656
  function normalizeManagerState(value) {
@@ -1378,52 +1382,35 @@ async function listProviderCandidates(options = {}) {
1378
1382
  const candidates = [];
1379
1383
  const user = userRoots();
1380
1384
  const userScans = await scanDeduplicatedUserRoots(user);
1381
- for (const root of user) {
1382
- const scanned = userScans.get(root.key);
1383
- for (const entry of scanned.entries) {
1384
- if (!entry.loadable) continue;
1385
- const policy = effectiveSkillPolicy(policyResult, root, entry);
1386
- const needsOverlay = root.key !== "dsh" || policy.override !== void 0 || !entry.invocationPolicyValid || policyResult.writable === false;
1387
- if (!needsOverlay) continue;
1388
- candidates.push({
1389
- name: entry.declaredName,
1390
- description: entry.description,
1391
- invocation: { modelInvocable: policy.modelInvocable, userInvocable: policy.userInvocable },
1392
- provider: "dsh-skills-manager-external",
1393
- source: root.key === "dsh" ? "user-dsh" : `agent-${root.key}`,
1394
- rank: root.key === "dsh" ? USER_DSH_POLICY_RANK : entry.providerRank ?? root.rank,
1395
- locator: { rootKey: root.key, entryName: entry.name, path: entry.docPath, realEntryPath: entry.realEntryPath, realDocPath: entry.realDocPath },
1396
- resourceBase: { kind: "directory", path: entry.kind === "bundle" ? entry.realEntryPath : root.path },
1397
- path: entry.docPath,
1398
- metadata: { dshSkillsManager: { root: root.key, readOnly: !root.mutable, sourceReadOnly: !root.mutable, policyOnly: root.key === "dsh" } }
1399
- });
1400
- }
1401
- }
1385
+ const items = user.flatMap((root) => userScans.get(root.key).entries.map((entry) => ({ root, entry })));
1402
1386
  const cwd = options && typeof options.cwd === "string" ? options.cwd : void 0;
1403
1387
  if (cwd) {
1404
1388
  const roots = await projectRoots([cwd]);
1405
1389
  for (const root of roots) {
1406
1390
  const scanned = await scanEntries(root.path);
1407
- for (const entry of scanned.entries) {
1408
- if (!entry.loadable) continue;
1409
- const policy = effectiveSkillPolicy(policyResult, root, entry);
1410
- const needsOverlay = policy.override !== void 0 || !entry.invocationPolicyValid || policyResult.writable === false;
1411
- if (!needsOverlay) continue;
1412
- candidates.push({
1413
- name: entry.declaredName,
1414
- description: entry.description,
1415
- invocation: { modelInvocable: policy.modelInvocable, userInvocable: policy.userInvocable },
1416
- provider: "dsh-skills-manager-external",
1417
- source: root.kind,
1418
- rank: root.rank - 1,
1419
- locator: { rootKey: root.key, entryName: entry.name, path: entry.docPath, realEntryPath: entry.realEntryPath, realDocPath: entry.realDocPath },
1420
- resourceBase: { kind: "directory", path: entry.kind === "bundle" ? entry.realEntryPath : root.path },
1421
- path: entry.docPath,
1422
- metadata: { dshSkillsManager: { root: root.key, readOnly: !root.mutable, sourceReadOnly: !root.mutable, policyOnly: true } }
1423
- });
1424
- }
1391
+ for (const entry of scanned.entries) items.push({ root, entry });
1425
1392
  }
1426
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
+ }
1427
1414
  return candidates;
1428
1415
  }
1429
1416
  async function getProviderSkill(candidate, options = {}) {
@@ -1460,18 +1447,24 @@ async function getProviderSkill(candidate, options = {}) {
1460
1447
  function canonicalSkillName(item) {
1461
1448
  return item.entry.declaredName || item.entry.name;
1462
1449
  }
1463
- function markWinners(items, options = {}) {
1464
- const winners = /* @__PURE__ */ new Map();
1450
+ function groupLoadableSkillsByName(items) {
1451
+ const groups = /* @__PURE__ */ new Map();
1465
1452
  for (const item of [...items].sort((a, b) => a.root.rank - b.root.rank)) {
1466
- const canonicalName = canonicalSkillName(item);
1467
1453
  if (!item.entry.loadable) continue;
1468
- if (!winners.has(canonicalName)) {
1469
- winners.set(canonicalName, item);
1470
- } else if (options.markShadowed !== false) {
1471
- item.view.shadowedBy = { root: winners.get(canonicalName).root.key, name: winners.get(canonicalName).entry.name };
1472
- }
1454
+ const name = canonicalSkillName(item);
1455
+ const group = groups.get(name) || [];
1456
+ group.push(item);
1457
+ groups.set(name, group);
1473
1458
  }
1474
- 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
+ }
1475
1468
  if (options.markWinner !== false) winner.view.winner = true;
1476
1469
  winner.view.enabled = winner.policy.enabled;
1477
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.37",
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",