dsh-skill-hub 0.3.13 → 0.3.15

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.
Files changed (60) hide show
  1. package/CONTRIBUTING.md +6 -4
  2. package/README.md +9 -5
  3. package/README.zh.md +9 -5
  4. package/lib/client.js +55 -58
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +529 -315
  7. package/lib/types/client/SkillHubSettingsCard.d.ts +5 -3
  8. package/lib/types/client/icons.d.ts +4 -5
  9. package/lib/types/client/index.d.ts +13 -12
  10. package/lib/types/client/locales/market.d.ts +1 -0
  11. package/lib/types/client/locales.d.ts +1 -0
  12. package/lib/types/client/settings-form.d.ts +9 -3
  13. package/lib/types/client/slash-dots.d.ts +20 -11
  14. package/lib/types/index.d.ts +59 -24
  15. package/lib/types/protocol/api.d.ts +22 -2
  16. package/lib/types/protocol/config.d.ts +30 -4
  17. package/lib/types/protocol/market.d.ts +1 -1
  18. package/lib/types/protocol/repo.d.ts +18 -1
  19. package/lib/types/protocol.d.ts +3 -3
  20. package/lib/types/repo/discovery.d.ts +59 -3
  21. package/lib/types/repo/github-client.d.ts +16 -0
  22. package/lib/types/repo.d.ts +6 -3
  23. package/lib/types/routes/helpers.d.ts +8 -1
  24. package/lib/types/routes/market.d.ts +2 -2
  25. package/lib/types/routes.d.ts +1 -1
  26. package/lib/types/store/store.d.ts +4 -0
  27. package/package.json +31 -41
  28. package/src/client/SkillHubSettingsCard.tsx +5 -3
  29. package/src/client/icons.tsx +4 -5
  30. package/src/client/index.tsx +33 -32
  31. package/src/client/locales/market.ts +2 -0
  32. package/src/client/panel/RepoScanCard.tsx +6 -3
  33. package/src/client/panel/SkillHubPanel.tsx +1 -1
  34. package/src/client/panel/SourcesView.tsx +1 -1
  35. package/src/client/panel/hooks/useGroupFlow.ts +1 -8
  36. package/src/client/settings-form.ts +9 -4
  37. package/src/client/slash-dots.test.ts +19 -36
  38. package/src/client/slash-dots.tsx +30 -60
  39. package/src/index.ts +145 -82
  40. package/src/protocol/api.ts +24 -2
  41. package/src/protocol/config.ts +35 -4
  42. package/src/protocol/market.ts +1 -1
  43. package/src/protocol/repo.ts +22 -1
  44. package/src/protocol.ts +3 -3
  45. package/src/repo/discovery.ts +120 -33
  46. package/src/repo/github-client.ts +18 -1
  47. package/src/repo/install.ts +8 -7
  48. package/src/repo.test.ts +158 -4
  49. package/src/repo.ts +10 -2
  50. package/src/routes/config.ts +19 -2
  51. package/src/routes/helpers.ts +9 -2
  52. package/src/routes/market.ts +49 -37
  53. package/src/routes/repo-import.ts +3 -2
  54. package/src/routes/sources.ts +2 -1
  55. package/src/routes.test.ts +107 -1
  56. package/src/routes.ts +21 -3
  57. package/src/store/migrate.ts +3 -2
  58. package/src/store/store.ts +7 -2
  59. package/src/store.test.ts +50 -0
  60. package/src/update.ts +4 -2
package/CONTRIBUTING.md CHANGED
@@ -39,7 +39,7 @@ src/client/locales/ dictionaries by view (common/skills/market/sources/detai
39
39
  ```bash
40
40
  npm install
41
41
  npm run typecheck # tsc --noEmit
42
- npm test # vitest (233 tests across 14 suites)
42
+ npm test # vitest (255 tests across 14 suites)
43
43
  npm run build # tsc declarations + tsdown bundles (lib/index.js + lib/client.js)
44
44
  ```
45
45
 
@@ -53,8 +53,8 @@ npm run build # tsc declarations + tsdown bundles (lib/index.js + lib/clie
53
53
  live GUI.
54
54
  3. **Build** — `npm run build` must produce `lib/index.js` and `lib/client.js`.
55
55
  4. **Keep the diff focused** — one logical change per PR, with a clear title and description.
56
- 5. **Documentation** — update `README.md` (including the embedded Chinese collapsible section) when
57
- behavior or the API surface changes.
56
+ 5. **Documentation** — update `README.md` **and** `README.zh.md` (both ship with the package and are
57
+ kept in sync) when behavior or the API surface changes.
58
58
 
59
59
  ## Code style
60
60
 
@@ -68,7 +68,9 @@ npm run build # tsc declarations + tsdown bundles (lib/index.js + lib/clie
68
68
  ```bash
69
69
  # after a change:
70
70
  npm run build
71
- # restart the dsh web process, then verify Settings → 技能 and Settings → 插件 → Skill Hub
71
+ # restart the dsh web process, then verify both surfaces:
72
+ # Settings → 技能 — the skill hub panel
73
+ # sidebar 插件 → dsh-skill-hub — the plugin's settings card
72
74
  ```
73
75
 
74
76
  When the web profile installs this repo as a link (`"dsh-skill-hub": "link:/path/to/repo"`, the
package/README.md CHANGED
@@ -24,7 +24,9 @@ dsh plugin --profile web add dsh-skill-hub
24
24
  # restart dsh web → Settings → 技能 → Market → scan → import
25
25
  ```
26
26
 
27
- Requires `Node ^22.19 || >=24` + dsh web (`0.1.6-alpha.1`, `0.1.x` forward compatible).
27
+ Requires `Node ^22.19 || >=24` + dsh web `>=0.1.7-alpha.1 <0.2`.
28
+
29
+ Two surfaces — the hub panel lives at **Settings → 技能**, and this plugin's own configuration card sits on the plugin's page in the **Plugins manager** (sidebar → 插件 → dsh-skill-hub). The card is registered by the plugin itself into the `plugins.bundle.config` slot — dsh has no auto-generated config page — and covers the master switch, announce-to-agent, dot colors, usage display and the stats window/interval.
28
30
 
29
31
  ## Features
30
32
 
@@ -35,10 +37,10 @@ Requires `Node ^22.19 || >=24` + dsh web (`0.1.6-alpha.1`, `0.1.x` forward compa
35
37
  - **Organize** — scenes (tags) plus auto-aggregated source collections, all drag-reorderable and persisted in `~/.dsh/dsh-skill-hub.json`. Edit mode reveals delete/reorder without cluttering the read view.
36
38
  - **Diagnose & fix** — files the provider skips (missing frontmatter, bad YAML, name mismatch, short description) show up with reasons; auto-fixable ones (e.g. unquoted `:` in descriptions) get a one-click Fix button.
37
39
  - **Scaffold** — new-skill wizard writing to `~/.dsh/skills` or `~/.agents/skills` (`SKILL.md` template below).
38
- - **Market** — built-in curated repos plus custom `owner/repo` sources. Any top-level directory containing `SKILL.md` scans as a root (no allowlist). Async import with byte-level progress and cancel. Each source pins a version — click the ref badge to switch between releases, branches, or a custom ref.
40
+ - **Market** — built-in curated repos plus custom `owner/repo` sources. Any top-level directory containing `SKILL.md` scans as a root (no allowlist), and a `SKILL.md` at the repo root itself scans as a single skill named after the repo (minus repo tooling such as `.github/`). Async import with byte-level progress and cancel. Each source pins a version — click the ref badge to switch between releases, branches, or a custom ref.
39
41
  - **Track updates** — imported skills record a repo + commit snapshot. Check all / update-all, per-source badges (installed / updatable / deleted upstream / new release). Sync overwrites local edits (with confirm); upstream deletions move into a restorable trash that keeps source and scene membership.
40
42
  - **Stats** — per-skill call counts + last-used times from session logs (incremental cache), group summaries; window and scan interval live-configurable from the settings card.
41
- - **Settings card** — master switch, announce-to-agent, invocation dot colors, usage display toggles, stats window/interval; plus a self-update check against GitHub releases.
43
+ - **Settings card** — registered on the plugin's own page in the **Plugins manager** (sidebar → 插件 → dsh-skill-hub; collapsed until you expand the title): master switch, announce-to-agent, invocation dot colors, usage display toggles, stats window/interval.
42
44
 
43
45
  ## Why not just the read-only browser?
44
46
 
@@ -92,7 +94,7 @@ Host uses only `ctx.skills.snapshot/get`, `ctx.webServer.register`, `ctx.systemP
92
94
  | `/api/skill-hub/config` | GET/POST | runtime config (`null` clears) |
93
95
  | `/api/skill-hub/groups` | GET | tags + collections + orders |
94
96
  | `/api/skill-hub/tag` etc. | POST | create/rename, delete, set members, reorder |
95
- | `/api/skill-hub/market` etc. | GET/POST | list/add/delete/pin/check/sync sources |
97
+ | `/api/skill-hub/market` etc. | GET/POST | list/add/delete/pin sources; `…/source/check` + `…/source/sync` update them |
96
98
  | `/api/skill-hub/market/source/versions?repo=` | GET | releases + branches for the version picker |
97
99
  | `/api/skill-hub/repo?repo=` | GET | discover (any root) |
98
100
  | `/api/skill-hub/repo/import` | POST | async job `{jobId, total, totalBytes}` |
@@ -100,12 +102,13 @@ Host uses only `ctx.skills.snapshot/get`, `ctx.webServer.register`, `ctx.systemP
100
102
  | `/api/skill-hub/repo/import/cancel` | POST | cancel job |
101
103
  | `/api/skill-hub/sources` etc. | GET/POST | list/check/sync/delete/restore/clear trash |
102
104
  | `/api/skill-hub/update` | GET | plugin latest release |
105
+ | unknown `/api/skill-hub/*` | GET/POST | `404 {error}` naming the path — a typo must not read as an auth failure (the SPA fallback answers 401 for `/api/*` it does not own) |
103
106
 
104
107
  ## Development
105
108
 
106
109
  ```bash
107
110
  npm run typecheck # tsc --noEmit
108
- npm test # 176 tests, 9 suites
111
+ npm test # 255 tests, 14 suites
109
112
  npm run build # tsc + tsdown → lib/index.js + lib/client.js
110
113
  ```
111
114
 
@@ -117,6 +120,7 @@ npm run build # tsc + tsdown → lib/index.js + lib/client.js
117
120
  - Skill missing — check the diagnostics section (frontmatter / name mismatch / short description).
118
121
  - Empty source group — its skills were deleted, or their disabled records were lost (sidecar restored/hand-edited). Startup reconciles `.disabled` files on disk; source groups with no visible member are no longer rendered.
119
122
  - Dots missing in `/` menu — dsh internals changed; catalog still works.
123
+ - Settings card missing — open the plugin in the Plugins manager (sidebar → 插件 → dsh-skill-hub) and expand the 「技能中枢」 card at the top of the page (collapsed by default). On dsh older than `0.1.7-alpha.1` this plugin does not load at all (the settings API it targets does not exist there).
120
124
 
121
125
  ## Community
122
126
 
package/README.zh.md CHANGED
@@ -24,7 +24,9 @@ dsh plugin --profile web add dsh-skill-hub
24
24
  # 重启 dsh web → 设置 → 技能 → 市场 → 扫描 → 导入
25
25
  ```
26
26
 
27
- 要求 `Node ^22.19 || >=24` + dsh web(`0.1.6-alpha.1`,兼容后续 `0.1.x`)。
27
+ 要求 `Node ^22.19 || >=24` + dsh web `>=0.1.7-alpha.1 <0.2`。
28
+
29
+ 两个界面 —— 管理面板在 **设置 → 技能**;本插件自身的配置卡片在 **插件管理页** 的插件自有页面上(侧边栏「插件」→ dsh-skill-hub)。该卡片由本插件自己注册进 `plugins.bundle.config` 槽位(dsh 没有自动生成的配置页),覆盖总开关、向 Agent 公告、圆点颜色、用量显示与统计窗口/间隔。
28
30
 
29
31
  ## 功能
30
32
 
@@ -35,10 +37,10 @@ dsh plugin --profile web add dsh-skill-hub
35
37
  - **整理** — 场景(tag)+ 自动聚合的来源集合,全部可拖拽排序并持久化到 `~/.dsh/dsh-skill-hub.json`。编辑模式收敛删除/排序控件,阅读视图保持干净。
36
38
  - **诊断与修复** — provider 跳过的文件给出原因(缺 frontmatter、YAML 非法、名称不一致、描述过短);可自动修复的(如描述里未加引号的 `:`)一键 Fix 落盘。
37
39
  - **新建** — 新技能向导,写入 `~/.dsh/skills` 或 `~/.agents/skills`(`SKILL.md` 模板见下)。
38
- - **市场** — 内置精选仓库 + 自定义 `owner/repo`。任何含 `SKILL.md` 的顶层目录都可扫描(无白名单)。异步导入,字节级进度+取消。每个源钉一个版本 —— 点 ref 徽标可在发布版/分支/手输之间切换。
40
+ - **市场** — 内置精选仓库 + 自定义 `owner/repo`。任何含 `SKILL.md` 的顶层目录都可扫描(无白名单);`SKILL.md` 直接放在仓库根时,整个仓库算一个技能,技能名取仓库名(`.github/` 等仓库基建不计入)。异步导入,字节级进度+取消。每个源钉一个版本 —— 点 ref 徽标可在发布版/分支/手输之间切换。
39
41
  - **跟踪更新** — 导入的技能记录 repo+commit 快照。检查全部/一键全更;每源徽章(已装/可更新/上游已删/新版本)。同步覆盖本地修改(先确认);上游删除跟进移入回收站,恢复保留来源与场景归属。
40
42
  - **统计** — 会话日志的调用次数+最近使用,分组头汇总;窗口与扫描间隔在设置卡片实时可调。
41
- - **设置卡片** — 总开关、向 Agent 公告、调用圆点颜色、用量显示开关、统计窗口/间隔;附带插件自更新检查(对 GitHub releases)。
43
+ - **设置卡片** — 注册在 **插件管理页** 的插件自有页面上(侧边栏「插件」→ dsh-skill-hub,默认折叠,点标题展开):总开关、向 Agent 公告、调用圆点颜色、用量显示开关、统计窗口/间隔。
42
44
 
43
45
  ## 为什么还需要一个管理器?
44
46
 
@@ -92,7 +94,7 @@ GitHub 仓库 ──扫描/导入──▶ ~/.dsh/skills
92
94
  | `/api/skill-hub/config` | GET/POST | 运行时配置(`null` 清除) |
93
95
  | `/api/skill-hub/groups` | GET | tags+集合+排序 |
94
96
  | `/api/skill-hub/tag` 等 | POST | 新建/重命名、删除、设成员、排序 |
95
- | `/api/skill-hub/market` 等 | GET/POST | 市场源 列表/添加/删除/钉 ref/检查/同步 |
97
+ | `/api/skill-hub/market` 等 | GET/POST | 市场源 列表/添加/删除/钉 ref;`…/source/check` + `…/source/sync` 检查与同步 |
96
98
  | `/api/skill-hub/market/source/versions?repo=` | GET | 版本选择器的 releases+branches |
97
99
  | `/api/skill-hub/repo?repo=` | GET | 发现(任意根) |
98
100
  | `/api/skill-hub/repo/import` | POST | 异步任务 `{jobId, total, totalBytes}` |
@@ -100,12 +102,13 @@ GitHub 仓库 ──扫描/导入──▶ ~/.dsh/skills
100
102
  | `/api/skill-hub/repo/import/cancel` | POST | 取消任务 |
101
103
  | `/api/skill-hub/sources` 等 | GET/POST | 来源 列表/检查/同步/删除/恢复/清空回收站 |
102
104
  | `/api/skill-hub/update` | GET | 插件最新发布 |
105
+ | 未知的 `/api/skill-hub/*` | GET/POST | `404 {error}` 且写明路径 —— 拼错路径不该被读成鉴权失败(宿主 SPA fallback 对自己不拥有的 `/api/*` 一律回 401) |
103
106
 
104
107
  ## 开发
105
108
 
106
109
  ```bash
107
110
  npm run typecheck # tsc --noEmit
108
- npm test # 176 tests, 9 suites
111
+ npm test # 255 tests, 14 suites
109
112
  npm run build # tsc + tsdown → lib/index.js + lib/client.js
110
113
  ```
111
114
 
@@ -117,6 +120,7 @@ npm run build # tsc + tsdown → lib/index.js + lib/client.js
117
120
  - 技能不出现 — 看诊断区(缺 frontmatter / 名称不一致 / 描述过短)。
118
121
  - 来源组空壳 — 组内技能已被删除、或禁用记录丢失(状态文件被恢复/手改)。启动时自动对账磁盘上的 `.disabled` 文件补记录;无可见成员的来源组不再渲染。
119
122
  - `/` 菜单圆点消失 — dsh 内部触发源变更,目录功能不受影响。
123
+ - 找不到配置卡片 — 在插件管理页打开本插件(侧边栏「插件」→ dsh-skill-hub),展开页面顶部的「技能中枢」卡片(默认折叠)。dsh 低于 `0.1.7-alpha.1` 时本插件无法加载(它所用的 settings API 在旧版不存在)。
120
124
 
121
125
  ## 社区
122
126
 
package/lib/client.js CHANGED
@@ -7,7 +7,13 @@ window.__ModuleLoader__.load({
7
7
  let react = require("react");
8
8
  let react_jsx_runtime = require("react/jsx-runtime");
9
9
  //#region src/protocol/api.ts
10
- /** Browser-facing base paths of the skill-hub API family. */
10
+ /**
11
+ * Browser-facing base paths of the skill-hub API family.
12
+ *
13
+ * Naming rule: a path's segments mirror its scope. Market sources own the
14
+ * `/market/source/*` subtree — add, delete, ref, versions, check, sync — so
15
+ * the update check and the sync that acts on its result sit side by side.
16
+ */
11
17
  const SKILL_HUB_API = {
12
18
  catalog: "/api/skill-hub/catalog",
13
19
  skill: "/api/skill-hub/skill",
@@ -21,7 +27,7 @@ window.__ModuleLoader__.load({
21
27
  marketSource: "/api/skill-hub/market/source",
22
28
  marketSourceDelete: "/api/skill-hub/market/source/delete",
23
29
  marketSourceRef: "/api/skill-hub/market/source/ref",
24
- marketCheck: "/api/skill-hub/market/check",
30
+ marketCheck: "/api/skill-hub/market/source/check",
25
31
  marketSync: "/api/skill-hub/market/source/sync",
26
32
  repo: "/api/skill-hub/repo",
27
33
  repoImport: "/api/skill-hub/repo/import",
@@ -46,6 +52,16 @@ window.__ModuleLoader__.load({
46
52
  marketStats: "/api/skill-hub/market/stats"
47
53
  };
48
54
  //#endregion
55
+ //#region src/protocol/config.ts
56
+ /**
57
+ * This plugin's Loader entry id — the settings namespace dsh serves its config
58
+ * under. It is cordis.patch.yml's insert id, NOT the package name, and it is
59
+ * shared by contract because the browser half addresses the same form through
60
+ * `ctx.configForms.get(HUB_ENTRY_ID)` while the host half writes through
61
+ * `ctx.settings.update(HUB_ENTRY_ID, …)`.
62
+ */
63
+ const HUB_ENTRY_ID = "skill-hub";
64
+ //#endregion
49
65
  //#region src/protocol/sources.ts
50
66
  /** 项目级技能来源(它们有 workspace 归属,不属于「个人」组)。 */
51
67
  function isProjectSource(source) {
@@ -480,6 +496,7 @@ window.__ModuleLoader__.load({
480
496
  "repo.cancel": "取消",
481
497
  "repo.cancelled": "已取消 · 已导入 {imported}/{total} 已保留,临时文件已清理",
482
498
  "repo.importingCurrent": "正在导入: {name} ({done}/{total})",
499
+ "repo.root.self": "仓库根",
483
500
  "repo.root.skills": "skills",
484
501
  "repo.root.designTemplates": "design-templates",
485
502
  "repo.root.generic": "{root}"
@@ -559,6 +576,7 @@ window.__ModuleLoader__.load({
559
576
  "repo.cancel": "Cancel",
560
577
  "repo.cancelled": "Cancelled · {imported}/{total} kept, temp files cleaned",
561
578
  "repo.importingCurrent": "Importing: {name} ({done}/{total})",
579
+ "repo.root.self": "Repo root",
562
580
  "repo.root.skills": "Skills",
563
581
  "repo.root.designTemplates": "Templates",
564
582
  "repo.root.generic": "{root}"
@@ -1164,36 +1182,11 @@ window.__ModuleLoader__.load({
1164
1182
  }
1165
1183
  }
1166
1184
  /**
1167
- * One menu-row dot element. Inline span so it needs no CSS module; the
1168
- * candidate menu centers it inside its 16×16 leading icon slot.
1169
- * @param color - the dot's background color.
1170
- * @returns a React node (memory-only, never crosses the Host boundary).
1171
- */
1172
- function dotIcon(color) {
1173
- return (0, react.createElement)("span", {
1174
- "aria-hidden": true,
1175
- style: {
1176
- display: "inline-block",
1177
- width: 6,
1178
- height: 6,
1179
- borderRadius: 3,
1180
- background: color,
1181
- flex: "none"
1182
- }
1183
- });
1184
- }
1185
- /**
1186
- * Wrap the core skill source so every menu row carries the invocation dot.
1187
- * Exported for unit tests; production wiring goes through setupSkillSlashDots.
1188
- * @param source - the registered `/skill` source.
1189
- * @param api - hub browser API for the modelInvocable lookup.
1190
- * @param scope - hub settings scope for the dot colors.
1191
- * @returns a disposer restoring the original candidates.
1192
- */
1193
- /**
1194
- * DOM 兜底:在 alpha.2 新版 MenuView(icon 仅枚举)下通过直接操作
1195
- * 已渲染的 `[role="option"]` 列表注入彩色点,绕过 `icon` 限制。
1196
- * 旧版仍走 `icon` 注入,此处仅为新版。
1185
+ * 通过直接操作已渲染的 `[role="option"]` 列表注入彩色点,
1186
+ * 绕过 MenuView 对 `icon` 的枚举限制。
1187
+ * @param modelByName - 技能名 → 模型可调用性;不在表内的候选不注点。
1188
+ * @param modelColor - 模型可调用技能的点色。
1189
+ * @param userColor - 仅用户可调用技能的点色。
1197
1190
  */
1198
1191
  function injectDotsViaDOM(modelByName, modelColor, userColor) {
1199
1192
  if (typeof document === "undefined" || typeof requestAnimationFrame === "undefined") return;
@@ -1231,25 +1224,24 @@ window.__ModuleLoader__.load({
1231
1224
  };
1232
1225
  requestAnimationFrame(() => setTimeout(run, 0));
1233
1226
  }
1227
+ /**
1228
+ * Wrap the core skill source so every menu row carries the invocation dot.
1229
+ * Exported for unit tests; production wiring goes through setupSkillSlashDots.
1230
+ * @param source - the registered `/skill` source.
1231
+ * @param api - hub browser API for the modelInvocable lookup.
1232
+ * @param scope - hub settings scope for the dot colors.
1233
+ * @returns a disposer restoring the original candidates.
1234
+ */
1234
1235
  function wrapSkillSource(source, api, scope) {
1235
1236
  const original = source.candidates;
1236
1237
  source.candidates = async (session, req) => {
1237
1238
  const items = await original(session, req);
1238
1239
  if (req.signal.aborted) return items;
1239
- const isNewHost = req !== null && typeof req === "object" && "drilled" in req;
1240
1240
  const modelByName = await modelInvocableMap(api);
1241
1241
  if (req.signal.aborted) return items;
1242
1242
  const snapshot = scope.getSnapshot();
1243
- const modelColor = snapshot.value?.dotModelColor ?? "#2f81f7";
1244
- const userColor = snapshot.value?.dotUserColor ?? "#3fb950";
1245
- if (isNewHost) {
1246
- injectDotsViaDOM(modelByName, modelColor, userColor);
1247
- return items;
1248
- }
1249
- return items.map((item) => ({
1250
- ...item,
1251
- icon: dotIcon(modelByName.get(item.name) ?? true ? modelColor : userColor)
1252
- }));
1243
+ injectDotsViaDOM(modelByName, snapshot.value?.dotModelColor ?? "#2f81f7", snapshot.value?.dotUserColor ?? "#3fb950");
1244
+ return items;
1253
1245
  };
1254
1246
  return () => {
1255
1247
  source.candidates = original;
@@ -2585,8 +2577,10 @@ window.__ModuleLoader__.load({
2585
2577
  * The dsh-skill-hub plugin settings card: bridges the hub's settings
2586
2578
  * namespace (bound through the official settings transport) onto the
2587
2579
  * family-style staged card form (enabled master switch + agent announcement).
2588
- * Registered into the official `settings.plugin.item` slot keyed by that
2589
- * namespace, so the plugin shows up in Settings → 插件 on dsh rc.7+.
2580
+ * Registered into `plugins.bundle.config` under the bundle's package name, so
2581
+ * the Plugins manager renders it on dsh-skill-hub's own page. That slot is
2582
+ * dispatched with `view: 'page'` only, so the card always renders its full
2583
+ * form (no `summary` one-liner variant).
2590
2584
  */
2591
2585
  /** Bridges the hub's config scope onto the card's staged form. */
2592
2586
  var SkillHubSettingsCardController = class {
@@ -3860,7 +3854,7 @@ window.__ModuleLoader__.load({
3860
3854
  const visible = visibleCollections(groupsState?.collections ?? [], sourceFiltered, catalog?.disabled ?? [], normalized, sourceFilter, origins);
3861
3855
  const collections = visible.map((entry) => entry.collection);
3862
3856
  const uncategorized = sourceFiltered.filter((skill) => origins[skill.name] === void 0 && !isProjectSource(skill.source));
3863
- const personalDisabled = (catalog?.disabled ?? []).filter((record) => origins[record.name] === void 0).filter((record) => normalized.length === 0 || record.name.toLocaleLowerCase().includes(normalized) || record.description.toLocaleLowerCase().includes(normalized)).filter((record) => sourceFilter === "all" || sourceFilter === "private");
3857
+ const personalDisabled = (catalog?.disabled ?? []).filter((record) => origins[record.name] === void 0).filter((record) => normalized.length === 0 || record.name.toLocaleLowerCase().includes(normalized) || record.description.toLocaleLowerCase().includes(normalized)).filter(() => sourceFilter === "all" || sourceFilter === "private");
3864
3858
  const allPersonalNames = [...uncategorized.map((s) => s.name), ...personalDisabled.map((r) => r.name)];
3865
3859
  const hasPersonal = allPersonalNames.length > 0;
3866
3860
  const defaultTopKeys = [
@@ -4761,8 +4755,11 @@ window.__ModuleLoader__.load({
4761
4755
  /**
4762
4756
  * 已知技能根的显示名键;未知根直接显示目录名。
4763
4757
  * zh 字典里这两项就是目录名本身(与接线前的硬编码文本一致)。
4758
+ * 空串是「技能就在仓库根」的哨兵根(host 侧 REPO_ROOT),目录名无从显示,
4759
+ * 给它一个可读标签,否则筛选按钮会是一片空白。
4764
4760
  */
4765
4761
  function rootLabelKey(root) {
4762
+ if (root === "") return "repo.root.self";
4766
4763
  if (root === "skills") return "repo.root.skills";
4767
4764
  if (root === "design-templates") return "repo.root.designTemplates";
4768
4765
  return "repo.root.generic";
@@ -4923,7 +4920,7 @@ window.__ModuleLoader__.load({
4923
4920
  " ",
4924
4921
  counts.get(root) ?? 0
4925
4922
  ]
4926
- }, root))
4923
+ }, root === "" ? "repo-root" : root))
4927
4924
  ]
4928
4925
  });
4929
4926
  })(),
@@ -5013,7 +5010,7 @@ window.__ModuleLoader__.load({
5013
5010
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5014
5011
  className: panel_module_css_default.rowDesc,
5015
5012
  children: [
5016
- entry.dir,
5013
+ entry.dir === "" ? tt("repo.root.self") : entry.dir,
5017
5014
  " · ",
5018
5015
  tt("repo.files", {
5019
5016
  count: entry.fileCount,
@@ -6941,7 +6938,7 @@ window.__ModuleLoader__.load({
6941
6938
  const [workspaceDraft, setWorkspaceDraft] = (0, react.useState)("");
6942
6939
  /** 「筛选」面板开合(来源 + 调用方式收进这里;搜索/排序/视图切换始终可见)。 */
6943
6940
  const [filtersOpen, setFiltersOpen] = (0, react.useState)(false);
6944
- const { catalog, loading, loadError, successBanner, updateState, detail, detailLoading, showForm, formName, formDesc, formRoot, formBusy, formMessage, hubConfig, tab, skillView, sourceFilter, sortKey, search, workspace, setWorkspace, sourcesState, tagBusy, batchBusy, busyNames, normalized, origins, sourceOptions, filtered, conflictDialog, confirmDialog, deleteSkillDialog, deleteGroupDialog, confirmClearTrash, branchChoice, branchBusy, marketSyncDialog, syncBusy, editingTag, editName, membersDraft, editSearch, uses, groupsState, sourceCheck, checkingSource, syncingSource, showLegend, editMode, setLoadError, setSuccessBanner, setDetail, setShowForm, setFormName, setFormDesc, setFormRoot, setFormMessage, setTab, setSkillView, setSourceFilter, setSortKey, setSearch, setConflictDialog, setConfirmDialog, setDeleteSkillDialog, setDeleteGroupDialog, setConfirmClearTrash, setBranchChoice, setMarketSyncDialog, setEditingTag, setEditName, setMembersDraft, setEditSearch, setShowLegend, setEditMode, checkUpdate, loadMarket, checkSources, requestSync, requestDelete, restoreTrash, clearTrash, runDeleteSkill, runDeleteGroup, runConfirmed, resolveConflict, confirmBranchChoice, confirmMarketSync, create, saveTag, deleteTag, enableDisabled } = hub;
6941
+ const { catalog, loading, loadError, successBanner, updateState, detail, detailLoading, showForm, formName, formDesc, formRoot, formBusy, formMessage, hubConfig, tab, skillView, sourceFilter, sortKey, search, workspace, setWorkspace, sourcesState, tagBusy, batchBusy, sourceOptions, filtered, conflictDialog, confirmDialog, deleteSkillDialog, deleteGroupDialog, confirmClearTrash, branchChoice, branchBusy, marketSyncDialog, syncBusy, editingTag, editName, membersDraft, editSearch, uses, groupsState, sourceCheck, checkingSource, syncingSource, showLegend, editMode, setLoadError, setSuccessBanner, setDetail, setShowForm, setFormName, setFormDesc, setFormRoot, setFormMessage, setTab, setSkillView, setSourceFilter, setSortKey, setSearch, setConflictDialog, setConfirmDialog, setDeleteSkillDialog, setDeleteGroupDialog, setConfirmClearTrash, setBranchChoice, setMarketSyncDialog, setEditingTag, setEditName, setMembersDraft, setEditSearch, setShowLegend, setEditMode, checkUpdate, loadMarket, checkSources, requestSync, requestDelete, restoreTrash, clearTrash, runDeleteSkill, runDeleteGroup, runConfirmed, resolveConflict, confirmBranchChoice, confirmMarketSync, create, saveTag, deleteTag, enableDisabled } = hub;
6945
6942
  const { shortenedCount, fixingPaths, clearListFilters } = hub;
6946
6943
  if (detail !== null) {
6947
6944
  const disabledRecord = catalog?.disabled.find((record) => record.name === detail.name);
@@ -7588,16 +7585,16 @@ window.__ModuleLoader__.load({
7588
7585
  /**
7589
7586
  * Required services (fiber inject waiting — the runtime must be up first).
7590
7587
  * `connection`/`remote` are the settings transport's own prerequisites
7591
- * (`ctx.settingsScope.bind` resolves them on the caller's fiber), and
7592
- * `settingsScope` is the namespace-scope binder itself; mirror the official
7593
- * settings-plugins inject list.
7588
+ * (`ctx.configForms.get` resolves them on the caller's fiber), and
7589
+ * `configForms` is the shared settings-form service itself; mirror the
7590
+ * official settings-plugins inject list.
7594
7591
  */
7595
7592
  const inject = [
7596
7593
  "slots",
7597
7594
  "locale",
7598
7595
  "connection",
7599
7596
  "remote",
7600
- "settingsScope",
7597
+ "configForms",
7601
7598
  "inputTriggers"
7602
7599
  ];
7603
7600
  /**
@@ -7611,15 +7608,15 @@ window.__ModuleLoader__.load({
7611
7608
  }), "dsh-skill-hub: dictionaries");
7612
7609
  const t = ctx.locale.bind(NS);
7613
7610
  const api = new SkillHubApi();
7614
- const scope = ctx.settingsScope.bind({ namespace: NS });
7611
+ const scope = ctx.configForms.get(HUB_ENTRY_ID);
7615
7612
  const settingsCard = new SkillHubSettingsCardController(scope);
7616
7613
  ctx.effect(() => setupSkillSlashDots(ctx, api, scope), "dsh-skill-hub: slash dots");
7617
- ctx.effect(() => ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
7618
- name: "settings.plugin.item",
7619
- key: NS,
7614
+ ctx.effect(() => ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
7615
+ name: "plugins.bundle.config",
7616
+ key: "dsh-skill-hub",
7620
7617
  locale: NS,
7621
7618
  inject: () => settingsCard.inject()
7622
- }, SkillHubSettingsCard)), "dsh-skill-hub: settings card");
7619
+ }, SkillHubSettingsCard)), "dsh-skill-hub: plugin config");
7623
7620
  ctx.effect(() => ctx.slots.inject("settings.section", () => ctx.slots.register({
7624
7621
  name: "settings.section",
7625
7622
  id: "skill-hub",