dsh-skill-hub 0.2.0 → 0.2.2
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/CONTRIBUTING.md +1 -1
- package/README.md +181 -249
- package/README.zh.md +275 -0
- package/lib/client.js +395 -257
- package/lib/client.js.map +1 -1
- package/lib/index.js +126 -26
- package/lib/types/client/SkillHubSettingsCard.d.ts +23 -17
- package/lib/types/client/api.d.ts +9 -3
- package/lib/types/client/grouping.d.ts +3 -0
- package/lib/types/client/index.d.ts +12 -6
- package/lib/types/client/locales.d.ts +8 -4
- package/lib/types/client/market-catalog.d.ts +1 -1
- package/lib/types/client/panel/MarketView.d.ts +5 -5
- package/lib/types/client/panel/SkillHubPanel.d.ts +1 -1
- package/lib/types/client/panel/SourcesView.d.ts +5 -3
- package/lib/types/client/panel/useSkillHub.d.ts +4 -0
- package/lib/types/client/settings-card.d.ts +5 -4
- package/lib/types/index.d.ts +12 -1
- package/lib/types/protocol.d.ts +23 -0
- package/lib/types/skillfs.d.ts +1 -1
- package/lib/types/update.d.ts +1 -1
- package/package.json +28 -27
- package/src/client/SkillHubSettingsCard.tsx +25 -14
- package/src/client/api.ts +9 -6
- package/src/client/grouping.test.ts +12 -0
- package/src/client/grouping.ts +10 -1
- package/src/client/index.tsx +25 -19
- package/src/client/locales.ts +16 -8
- package/src/client/market-catalog.ts +1 -1
- package/src/client/panel/MarketView.tsx +77 -64
- package/src/client/panel/SkillHubPanel.tsx +18 -1
- package/src/client/panel/SkillRow.tsx +17 -2
- package/src/client/panel/SourcesView.tsx +92 -5
- package/src/client/panel/panel.module.css +9 -1
- package/src/client/panel/useSkillHub.ts +25 -11
- package/src/client/settings-card.tsx +5 -4
- package/src/index.ts +78 -31
- package/src/protocol.ts +24 -0
- package/src/routes.test.ts +55 -0
- package/src/routes.ts +90 -10
- package/src/skillfs.ts +1 -1
- package/src/update.ts +1 -1
- package/lib/types/client/api-config-scope.d.ts +0 -48
- package/src/client/api-config-scope.test.ts +0 -79
- package/src/client/api-config-scope.ts +0 -119
package/lib/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
+
import { settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
2
3
|
import z from "schemastery";
|
|
3
4
|
import { mkdir, mkdtemp, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
5
|
import { basename, dirname, join, resolve } from "node:path";
|
|
@@ -842,7 +843,7 @@ async function findProjectRoot(cwd) {
|
|
|
842
843
|
}
|
|
843
844
|
/**
|
|
844
845
|
* Scan one writable root for files the provider ignores, so the GUI can
|
|
845
|
-
* show why a skill never appears
|
|
846
|
+
* show why a skill never appears — a
|
|
846
847
|
* missing frontmatter must be visible, not silent). .disabled files belong
|
|
847
848
|
* to the hub and are skipped.
|
|
848
849
|
*/
|
|
@@ -1493,7 +1494,7 @@ function repoSkillEntry(name, root, repo) {
|
|
|
1493
1494
|
//#endregion
|
|
1494
1495
|
//#region src/update.ts
|
|
1495
1496
|
/**
|
|
1496
|
-
* Self-update check for dsh-skill-hub
|
|
1497
|
+
* Self-update check for dsh-skill-hub
|
|
1497
1498
|
* query GitHub's latest release and compare it to the installed version.
|
|
1498
1499
|
*
|
|
1499
1500
|
* This stays dependency-free: Node's global fetch is used and the version
|
|
@@ -1794,13 +1795,72 @@ async function knownSkillNames(deps) {
|
|
|
1794
1795
|
for (const disabled of await deps.store.listDisabled()) names.add(disabled.name);
|
|
1795
1796
|
return names;
|
|
1796
1797
|
}
|
|
1797
|
-
/**
|
|
1798
|
+
/**
|
|
1799
|
+
* 读取 dsh 的已知工作区清单(~/.dsh/storages/workspace.json 的
|
|
1800
|
+
* tables.workspaces 表)。面板默认视图据此合并所有工作区的项目技能;
|
|
1801
|
+
* 文件缺失/损坏时返回空清单(回退为仅用户级视图)。
|
|
1802
|
+
*/
|
|
1803
|
+
async function workspaceEntries(home) {
|
|
1804
|
+
try {
|
|
1805
|
+
const raw = await readFile(join(home, "storages", "workspace.json"), "utf8");
|
|
1806
|
+
const parsed = JSON.parse(raw);
|
|
1807
|
+
const tables = typeof parsed === "object" && parsed !== null ? parsed.tables : void 0;
|
|
1808
|
+
const workspaces = tables !== void 0 && typeof tables === "object" ? tables.workspaces : void 0;
|
|
1809
|
+
const entries = [];
|
|
1810
|
+
if (workspaces !== void 0 && typeof workspaces === "object") for (const record of Object.values(workspaces)) {
|
|
1811
|
+
const entry = record;
|
|
1812
|
+
if (entry !== null && typeof entry === "object" && typeof entry.path === "string" && entry.path !== "") entries.push({
|
|
1813
|
+
path: entry.path,
|
|
1814
|
+
title: typeof entry.title === "string" && entry.title !== "" ? entry.title : entry.path
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
return entries;
|
|
1818
|
+
} catch {
|
|
1819
|
+
return [];
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
/** 项目技能来源(workspace 字段只对它们设置)。 */
|
|
1823
|
+
function isProjectSource(source) {
|
|
1824
|
+
return source === "project-dsh" || source === "project-agents";
|
|
1825
|
+
}
|
|
1826
|
+
/**
|
|
1827
|
+
* Build the full catalog response (shared by catalog/toggle/create handlers).
|
|
1828
|
+
* 显式 cwd 只看该工作区;否则合并所有已知工作区(workspace.json)的项目技能
|
|
1829
|
+
* + 用户级技能,同名技能先到先得;没有任何工作区时回退为仅用户级视图。
|
|
1830
|
+
*/
|
|
1798
1831
|
async function buildCatalog(deps, cwd) {
|
|
1799
|
-
const lookup = cwd !== void 0 ? { cwd } : void 0;
|
|
1800
|
-
const snapshot = await deps.skills.snapshot(lookup);
|
|
1801
1832
|
const home = homeOf(deps);
|
|
1833
|
+
let workspaces;
|
|
1834
|
+
if (cwd !== void 0 && cwd !== "") workspaces = [{
|
|
1835
|
+
path: cwd,
|
|
1836
|
+
title: cwd
|
|
1837
|
+
}];
|
|
1838
|
+
else {
|
|
1839
|
+
workspaces = await workspaceEntries(home);
|
|
1840
|
+
if (workspaces.length === 0) workspaces = [{
|
|
1841
|
+
path: "",
|
|
1842
|
+
title: ""
|
|
1843
|
+
}];
|
|
1844
|
+
}
|
|
1845
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1846
|
+
let complete = true;
|
|
1847
|
+
for (const ws of workspaces) {
|
|
1848
|
+
const lookup = ws.path !== "" ? { cwd: ws.path } : void 0;
|
|
1849
|
+
const snapshot = await deps.skills.snapshot(lookup);
|
|
1850
|
+
if (!snapshot.complete) complete = false;
|
|
1851
|
+
for (const skill of snapshot.skills) {
|
|
1852
|
+
if (byName.has(skill.name)) continue;
|
|
1853
|
+
byName.set(skill.name, {
|
|
1854
|
+
skill,
|
|
1855
|
+
...isProjectSource(skill.source) && ws.path !== "" ? {
|
|
1856
|
+
workspace: ws.path,
|
|
1857
|
+
workspaceTitle: ws.title
|
|
1858
|
+
} : {}
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1802
1862
|
const timesByName = /* @__PURE__ */ new Map();
|
|
1803
|
-
await Promise.all(
|
|
1863
|
+
await Promise.all([...byName.values()].map(async ({ skill }) => {
|
|
1804
1864
|
if (!isWritableSource(skill.source)) return;
|
|
1805
1865
|
const base = rootPath(skill.source, home);
|
|
1806
1866
|
for (const candidate of [
|
|
@@ -1816,7 +1876,7 @@ async function buildCatalog(deps, cwd) {
|
|
|
1816
1876
|
return;
|
|
1817
1877
|
} catch {}
|
|
1818
1878
|
}));
|
|
1819
|
-
const skills =
|
|
1879
|
+
const skills = [...byName.values()].map(({ skill, workspace, workspaceTitle }) => {
|
|
1820
1880
|
const row = {
|
|
1821
1881
|
name: skill.name,
|
|
1822
1882
|
description: skill.description,
|
|
@@ -1826,7 +1886,12 @@ async function buildCatalog(deps, cwd) {
|
|
|
1826
1886
|
userInvocable: skill.invocation.userInvocable
|
|
1827
1887
|
},
|
|
1828
1888
|
provider: skill.provider,
|
|
1829
|
-
writable: isWritableSource(skill.source)
|
|
1889
|
+
writable: isWritableSource(skill.source),
|
|
1890
|
+
source: skill.source,
|
|
1891
|
+
...workspace !== void 0 ? {
|
|
1892
|
+
workspace,
|
|
1893
|
+
workspaceTitle: workspaceTitle ?? workspace
|
|
1894
|
+
} : {}
|
|
1830
1895
|
};
|
|
1831
1896
|
const times = timesByName.get(skill.name);
|
|
1832
1897
|
if (times !== void 0) {
|
|
@@ -1839,7 +1904,7 @@ async function buildCatalog(deps, cwd) {
|
|
|
1839
1904
|
const diagnostics = [...await scanDiagnostics("user-dsh", home), ...await scanDiagnostics("user-agents", home)];
|
|
1840
1905
|
return {
|
|
1841
1906
|
ok: true,
|
|
1842
|
-
complete
|
|
1907
|
+
complete,
|
|
1843
1908
|
skills,
|
|
1844
1909
|
disabled,
|
|
1845
1910
|
diagnostics
|
|
@@ -1944,7 +2009,15 @@ function makeRoutes(deps) {
|
|
|
1944
2009
|
return;
|
|
1945
2010
|
}
|
|
1946
2011
|
const cwd = queryParam(url, "cwd");
|
|
1947
|
-
|
|
2012
|
+
let skill;
|
|
2013
|
+
if (cwd !== void 0 && cwd !== "") skill = await deps.skills.get(name, { cwd });
|
|
2014
|
+
else {
|
|
2015
|
+
for (const ws of await workspaceEntries(homeOf(deps))) {
|
|
2016
|
+
skill = await deps.skills.get(name, { cwd: ws.path });
|
|
2017
|
+
if (skill !== void 0) break;
|
|
2018
|
+
}
|
|
2019
|
+
if (skill === void 0) skill = await deps.skills.get(name);
|
|
2020
|
+
}
|
|
1948
2021
|
if (skill === void 0) {
|
|
1949
2022
|
writeError(res, 404, "skill not found: " + name);
|
|
1950
2023
|
return;
|
|
@@ -3025,7 +3098,8 @@ const name = "skill-hub";
|
|
|
3025
3098
|
const inject = [
|
|
3026
3099
|
"webServer",
|
|
3027
3100
|
"skills",
|
|
3028
|
-
"systemPrompt"
|
|
3101
|
+
"systemPrompt",
|
|
3102
|
+
"settings"
|
|
3029
3103
|
];
|
|
3030
3104
|
const Config = z.object({
|
|
3031
3105
|
announceToAgent: z.boolean().default(HUB_CONFIG_DEFAULTS.announceToAgent),
|
|
@@ -3034,32 +3108,52 @@ const Config = z.object({
|
|
|
3034
3108
|
showUseTime: z.boolean().default(HUB_CONFIG_DEFAULTS.showUseTime),
|
|
3035
3109
|
showGroupSummary: z.boolean().default(HUB_CONFIG_DEFAULTS.showGroupSummary)
|
|
3036
3110
|
});
|
|
3111
|
+
/**
|
|
3112
|
+
* Settings namespace hosting the hub's runtime config. Since dsh rc.7 the
|
|
3113
|
+
* host serves every registered settings namespace to the web client (the
|
|
3114
|
+
* dsh-host-apiproxy allowlist is gone), so the browser card and the settings
|
|
3115
|
+
* page edit this namespace through the official settings transport, and the
|
|
3116
|
+
* plugin consumes the same resolved value — one source of truth.
|
|
3117
|
+
*/
|
|
3118
|
+
const CONFIG_NAMESPACE = settingsNamespace("dsh-skill-hub");
|
|
3119
|
+
/** Schema of the hub's settings namespace: the card's fields (booleans + optional dot colors). */
|
|
3120
|
+
const HubSettingsSchema = z.object({
|
|
3121
|
+
enabled: z.boolean().default(HUB_CONFIG_DEFAULTS.enabled),
|
|
3122
|
+
announceToAgent: z.boolean().default(HUB_CONFIG_DEFAULTS.announceToAgent),
|
|
3123
|
+
showUseCount: z.boolean().default(HUB_CONFIG_DEFAULTS.showUseCount),
|
|
3124
|
+
showUseTime: z.boolean().default(HUB_CONFIG_DEFAULTS.showUseTime),
|
|
3125
|
+
showGroupSummary: z.boolean().default(HUB_CONFIG_DEFAULTS.showGroupSummary),
|
|
3126
|
+
dotModelColor: z.string().pattern(HEX_COLOR_RE),
|
|
3127
|
+
dotUserColor: z.string().pattern(HEX_COLOR_RE)
|
|
3128
|
+
});
|
|
3037
3129
|
/** Order of the announcement section within the tool-guidance band. */
|
|
3038
3130
|
const SECTION_ORDER = 152;
|
|
3039
3131
|
/** Model-facing announcement: plugin presence, capabilities, and limits. */
|
|
3040
3132
|
const SKILL_HUB_GUIDANCE = ["本机已安装 dsh-skill-hub 插件(DSH Web GUI 技能中枢):设置 →「技能」分区为管理主页;设置 → 插件列表中有本插件的配置卡片(启用/公告开关)。能力:完整本地技能目录(项目/自定义/用户/内置全部来源,走官方 ctx.skills 注册表,含第三方 provider);按来源与自定义分组浏览,分组/来源头部的滑动开关可一键启用/禁用整组(跨组冲突时询问);市场:内置市场目录(精选仓库一键添加)加自定义仓库源,扫描后勾选安装,每个市场源行显示已装/可更新/上游已删数量,支持「检查全部」与「全部更新」;来源跟踪:从 GitHub 仓库(市场源或直接地址)导入的技能记录上游 repo/commit 快照,可检查更新、选择同步、上游删除时跟进删除(移入回收站可恢复,恢复后保留来源与场景归属);个人技能(无来源记录)不跟踪;调用次数与最近使用时间统计;查看技能正文;发现诊断;新建技能向导(写入 ~/.dsh/skills 或 ~/.agents/skills)。限制:仅用户级技能(user-dsh/user-agents 根目录)可写,项目/内置/运行时技能只读展示;路由仅回环可访问。用户提到「技能管理 / 技能列表 / 技能开关 / 技能同步 / 技能市场 / 更新技能 / 新建技能」时即指本插件,请据此协作。", "The dsh-skill-hub plugin is installed (the DSH Web GUI skill hub): Settings → \"Skills\" is the management page; Settings → Plugins lists this plugin's configuration card (enable / announcement toggles). Capabilities: full local skill catalog (project / custom / user / bundled roots via the official ctx.skills registry, including third-party providers); browsing by source and custom groups, each group header carrying a sliding switch to enable/disable the whole group in one click (cross-group conflicts prompt the user); market: a built-in catalog of curated repos (one-click add) plus custom repo sources, scan-and-install import, per-source installed / updatable / deleted-upstream badges with \"check all\" and \"update all\" actions; upstream source tracking: skills imported from GitHub repos (market sources or direct URLs) record the repo/commit snapshot, support update checks, selective sync, and follow-up deletion when the upstream removes a skill (moves it into a restorable trash; restoring keeps the source and scene membership); personal skills (no source record) are never tracked; invocation counts and last-used times; skill body inspection; discovery diagnostics; new-skill wizard (writes to ~/.dsh/skills or ~/.agents/skills). Limits: only user-level skills (user-dsh/user-agents roots) are writable; project/bundled/runtime skills are read-only; routes are loopback-only. When the user mentions \"skill management / skill list / skill toggle / skill sync / skill market / update skills / new skill\", this plugin is what they mean — collaborate accordingly."].join("\n\n");
|
|
3041
3133
|
/**
|
|
3042
3134
|
* Mount the skill hub routes and announcement.
|
|
3043
|
-
* @param ctx - host plugin context carrying webServer/skills/systemPrompt.
|
|
3135
|
+
* @param ctx - host plugin context carrying webServer/skills/systemPrompt/settings.
|
|
3044
3136
|
* @param config - resolved plugin config (schema defaults applied by the loader).
|
|
3045
3137
|
*/
|
|
3046
3138
|
function apply(ctx, config) {
|
|
3047
3139
|
const base = config ?? {};
|
|
3048
|
-
|
|
3049
|
-
|
|
3140
|
+
const settingsScope = ctx.settings.register(CONFIG_NAMESPACE, HubSettingsSchema, { base });
|
|
3141
|
+
const current = () => settingsScope.get();
|
|
3050
3142
|
const store = new SkillHubStore();
|
|
3051
3143
|
let disposeRoutes;
|
|
3052
3144
|
let disposeSection;
|
|
3053
3145
|
let disposeProvider;
|
|
3054
3146
|
let providerControl;
|
|
3055
3147
|
let stats;
|
|
3148
|
+
const saved = () => {
|
|
3149
|
+
return ctx.settings.describe().find((entry) => entry.ns === CONFIG_NAMESPACE)?.user ?? {};
|
|
3150
|
+
};
|
|
3056
3151
|
const updateConfig = async (patch) => {
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
return next;
|
|
3152
|
+
const user = { ...saved() };
|
|
3153
|
+
for (const [key, value] of Object.entries(patch)) if (value === void 0) delete user[key];
|
|
3154
|
+
else user[key] = value;
|
|
3155
|
+
await settingsScope.replace(user);
|
|
3156
|
+
return settingsScope.get();
|
|
3063
3157
|
};
|
|
3064
3158
|
const sync = () => {
|
|
3065
3159
|
if (disposeSection !== void 0) {
|
|
@@ -3096,7 +3190,7 @@ function apply(ctx, config) {
|
|
|
3096
3190
|
},
|
|
3097
3191
|
stats,
|
|
3098
3192
|
config: current,
|
|
3099
|
-
saved
|
|
3193
|
+
saved,
|
|
3100
3194
|
updateConfig
|
|
3101
3195
|
}).map((route) => ctx.webServer.register(route));
|
|
3102
3196
|
return () => {
|
|
@@ -3105,15 +3199,21 @@ function apply(ctx, config) {
|
|
|
3105
3199
|
}, "dsh-skill-hub: routes");
|
|
3106
3200
|
};
|
|
3107
3201
|
sync();
|
|
3108
|
-
|
|
3109
|
-
savedState = saved;
|
|
3110
|
-
current = () => resolveHubConfig(saved, base);
|
|
3202
|
+
ctx.effect(() => settingsScope.watch(() => {
|
|
3111
3203
|
sync();
|
|
3112
|
-
});
|
|
3204
|
+
}), "dsh-skill-hub: settings config watch");
|
|
3205
|
+
(async () => {
|
|
3206
|
+
const legacy = await store.getConfig();
|
|
3207
|
+
if (Object.keys(legacy).length > 0 && Object.keys(saved()).length === 0) try {
|
|
3208
|
+
await settingsScope.update(legacy);
|
|
3209
|
+
} catch (error) {
|
|
3210
|
+
ctx.logger.warn("[dsh-skill-hub] sidecar config migration into the settings namespace failed", error);
|
|
3211
|
+
}
|
|
3212
|
+
})();
|
|
3113
3213
|
ctx.inject(["sessionQuery"], (sctx) => {
|
|
3114
3214
|
stats = createSkillStatsReader(sctx.sessionQuery);
|
|
3115
3215
|
sync();
|
|
3116
3216
|
});
|
|
3117
3217
|
}
|
|
3118
3218
|
//#endregion
|
|
3119
|
-
export { Config, SKILL_HUB_GUIDANCE, apply, inject, name };
|
|
3219
|
+
export { CONFIG_NAMESPACE, Config, HubSettingsSchema, SKILL_HUB_GUIDANCE, apply, inject, name };
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The dsh-skill-hub plugin settings card: bridges the hub's
|
|
3
|
-
* (
|
|
4
|
-
* form (enabled master switch + agent announcement).
|
|
5
|
-
* official `settings.plugin.item` slot
|
|
6
|
-
*
|
|
7
|
-
* service (the host refuses third-party namespaces).
|
|
2
|
+
* The dsh-skill-hub plugin settings card: bridges the hub's settings
|
|
3
|
+
* namespace (bound through the official settings transport) onto the
|
|
4
|
+
* family-style staged card form (enabled master switch + agent announcement).
|
|
5
|
+
* Registered into the official `settings.plugin.item` slot keyed by that
|
|
6
|
+
* namespace, so the plugin shows up in Settings → 插件 on dsh rc.7+.
|
|
8
7
|
*/
|
|
9
8
|
import type { ReactElement } from 'react';
|
|
10
9
|
import type { SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client';
|
|
11
|
-
import {
|
|
10
|
+
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
11
|
+
import { type CardShell, type FieldState, type FormScope } from './settings-form.ts';
|
|
12
|
+
/** Model-invocable dot color default. Single source for the TS side; the
|
|
13
|
+
* panel's CSS mirrors it via --hub-model (panel.module.css). */
|
|
14
|
+
export declare const DEFAULT_DOT_MODEL_COLOR = "#2f81f7";
|
|
15
|
+
/** User-invocable dot color default. Single source for the TS side; the
|
|
16
|
+
* panel's CSS mirrors it via --hub-user (panel.module.css). */
|
|
17
|
+
export declare const DEFAULT_DOT_USER_COLOR = "#3fb950";
|
|
12
18
|
/** The card's projected state. */
|
|
13
19
|
export interface SkillHubSettingsState extends CardShell {
|
|
14
20
|
enabled: FieldState;
|
|
@@ -19,31 +25,31 @@ export interface SkillHubSettingsState extends CardShell {
|
|
|
19
25
|
showUseTime: FieldState;
|
|
20
26
|
showGroupSummary: FieldState;
|
|
21
27
|
}
|
|
22
|
-
/**
|
|
23
|
-
export interface
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
/** The business face the card's slot registration injects. */
|
|
29
|
+
export interface SkillHubSettingsCardFace {
|
|
30
|
+
hooks: {
|
|
31
|
+
/** The card's snapshot store (projected form state). */
|
|
32
|
+
skillHubSettingsCard: SnapshotStore<SkillHubSettingsState>;
|
|
33
|
+
};
|
|
26
34
|
save: () => void;
|
|
27
35
|
discard: () => void;
|
|
28
36
|
edit: (field: string, text: string) => void;
|
|
29
37
|
resetField: (field: string) => void;
|
|
30
38
|
}
|
|
39
|
+
/** Props the slot renderer binds (locale copy + injected form actions). */
|
|
40
|
+
export type SkillHubSettingsCardProps = PropsRuntime<'settings.plugin.item'> & PropsLocale<'dsh-skill-hub'> & InjectFace<SkillHubSettingsCardFace>;
|
|
31
41
|
/** Bridges the hub's config scope onto the card's staged form. */
|
|
32
42
|
export declare class SkillHubSettingsCardController {
|
|
33
43
|
private readonly form;
|
|
34
44
|
private readonly store;
|
|
35
|
-
/** @param scope - the hub
|
|
45
|
+
/** @param scope - the hub settings scope the card edits (FormScope-compatible). */
|
|
36
46
|
constructor(scope: FormScope);
|
|
37
47
|
private projection;
|
|
38
48
|
/**
|
|
39
49
|
* Build the face the card's slot registration injects.
|
|
40
50
|
* @returns the card's snapshot hook and its form actions.
|
|
41
51
|
*/
|
|
42
|
-
inject():
|
|
43
|
-
hooks: {
|
|
44
|
-
skillHubSettingsCard: SnapshotStore<SkillHubSettingsState>;
|
|
45
|
-
};
|
|
46
|
-
} & ReturnType<CardForm['actions']>;
|
|
52
|
+
inject(): SkillHubSettingsCardFace;
|
|
47
53
|
}
|
|
48
54
|
/**
|
|
49
55
|
* Render the dsh-skill-hub card.
|
|
@@ -9,8 +9,14 @@ export declare class SkillHubApiError extends Error {
|
|
|
9
9
|
}
|
|
10
10
|
/** The browser half's only data entry point. */
|
|
11
11
|
export declare class SkillHubApi {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
/** Catalog lookup options (cwd selects a workspace's project skills). */
|
|
13
|
+
catalog(options?: {
|
|
14
|
+
cwd?: string;
|
|
15
|
+
}): Promise<CatalogResponse>;
|
|
16
|
+
/** One skill's detail (cwd selects a workspace's project skills). */
|
|
17
|
+
skill(name: string, options?: {
|
|
18
|
+
cwd?: string;
|
|
19
|
+
}): Promise<SkillDetail>;
|
|
14
20
|
/** Move one writable skill into the restorable trash. */
|
|
15
21
|
deleteSkill(name: string): Promise<SkillDeleteResponse>;
|
|
16
22
|
/** Toggle one skill; resolves with the fresh catalog from the route. */
|
|
@@ -19,7 +25,7 @@ export declare class SkillHubApi {
|
|
|
19
25
|
stats(): Promise<StatsResponse>;
|
|
20
26
|
/** Toggle a whole group in one write; resolves with the fresh catalog + failures. */
|
|
21
27
|
toggleBatch(names: string[], enabled: boolean): Promise<ToggleBatchResponse>;
|
|
22
|
-
/** The user's added market sources
|
|
28
|
+
/** The user's added market sources. */
|
|
23
29
|
market(): Promise<MarketSourcesResponse>;
|
|
24
30
|
/** Add a market source repo; resolves with the fresh list. */
|
|
25
31
|
addMarketSource(repo: string): Promise<MarketSourcesResponse>;
|
|
@@ -40,11 +40,14 @@ export declare function conflictsOnClose(members: readonly string[], enabledName
|
|
|
40
40
|
}>): string[];
|
|
41
41
|
/** Origin-repo filter value: skills with no source record (private skills). */
|
|
42
42
|
export declare const PRIVATE_SOURCE = "private";
|
|
43
|
+
/** 项目级技能来源(它们有 workspace 归属,不属于「个人」组)。 */
|
|
44
|
+
export declare function isProjectSource(source: string): boolean;
|
|
43
45
|
/**
|
|
44
46
|
* Apply the origin filter ('all' or a specific origin repo; skills without a
|
|
45
47
|
* source record count as PRIVATE_SOURCE). The origins map is the store's
|
|
46
48
|
* skillName → repo derivation, so filtering follows the tracked source
|
|
47
49
|
* records instead of the filesystem root a skill happens to live under.
|
|
50
|
+
* 项目级技能(有 workspace 归属)永远不算「个人」。
|
|
48
51
|
*/
|
|
49
52
|
export declare function filterBySource(skills: readonly CatalogSkill[], source: string, origins: Readonly<Record<string, string>>): CatalogSkill[];
|
|
50
53
|
/** Catalog sort keys offered by the filter bar. */
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* Registers the dsh-skill-hub locale dictionaries and mounts two Settings
|
|
6
6
|
* surfaces, both through official slots (no DOM injection):
|
|
7
7
|
* - a plugin-management card in the `settings.plugin.item` slot (Settings →
|
|
8
|
-
* 插件 → 可配置插件列表),
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* 插件 → 可配置插件列表), keyed by the hub's settings namespace and bound
|
|
9
|
+
* through the official settings transport (dsh rc.7 serves every
|
|
10
|
+
* registered namespace to the web client, and the tab dispatches cards by
|
|
11
|
+
* namespace) — the family-bucket card pattern (PluginSettingsCard +
|
|
12
|
+
* CardForm vendored from dsh-task-board);
|
|
13
13
|
* - a top-level Settings section (Settings → 技能) hosting the skill hub
|
|
14
14
|
* panel: catalog, search, enable/disable, diagnostics, new-skill form.
|
|
15
15
|
*
|
|
@@ -29,7 +29,13 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
29
29
|
'dsh-skill-hub': HubKey;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Required services (fiber inject waiting — the runtime must be up first).
|
|
34
|
+
* `connection`/`remote` are the settings transport's own prerequisites
|
|
35
|
+
* (`ctx.settingsScope.bind` resolves them on the caller's fiber), and
|
|
36
|
+
* `settingsScope` is the namespace-scope binder itself; mirror the official
|
|
37
|
+
* settings-plugins inject list.
|
|
38
|
+
*/
|
|
33
39
|
export declare const inject: string[];
|
|
34
40
|
/** Type-only surface (export discipline: no value exports beyond the plugin contract). */
|
|
35
41
|
export type { SkillHubPanelProps } from './panel/SkillHubPanel.tsx';
|
|
@@ -26,19 +26,19 @@ export declare const zh: {
|
|
|
26
26
|
readonly 'panel.emptyAll': "本地还没有技能。点击「新建技能」创建第一个。";
|
|
27
27
|
readonly 'panel.loading': "读取中…";
|
|
28
28
|
readonly 'panel.incomplete': "(目录可能不完整)";
|
|
29
|
-
readonly '
|
|
29
|
+
readonly 'panel.workspacePlaceholder': "工作区路径(回车应用)";
|
|
30
|
+
readonly 'panel.workspaceHint': "填写项目路径后,面板同时显示该项目 .dsh/skills 与 .agents/skills 下的技能(只读);留空回车恢复用户级视图。";
|
|
31
|
+
readonly 'panel.workspaceClear': "清除工作区";
|
|
30
32
|
readonly 'market.addPlaceholder': "owner/repo 或 https://github.com/…";
|
|
31
33
|
readonly 'market.addSource': "添加源";
|
|
32
34
|
readonly 'market.noSources': "还没有市场源。从下方内置市场添加,或输入一个 GitHub 仓库。";
|
|
33
|
-
readonly 'market.
|
|
35
|
+
readonly 'market.title': "市场";
|
|
34
36
|
readonly 'market.catalogHint': "精选技能仓库,添加后即可扫描安装,并持续检查更新。";
|
|
35
37
|
readonly 'market.catalog.anthropics': "Anthropic 官方技能合集(Claude Skills)";
|
|
36
38
|
readonly 'market.catalog.superpowers': "Superpowers 社区技能合集";
|
|
37
39
|
readonly 'market.catalog.mattpocock': "Matt Pocock 技能集";
|
|
38
40
|
readonly 'market.catalog.openDesign': "设计技能合集(design-templates)";
|
|
39
|
-
readonly 'market.added': "已添加";
|
|
40
41
|
readonly 'market.add': "添加";
|
|
41
|
-
readonly 'market.mySources': "我的市场源";
|
|
42
42
|
readonly 'market.installed': "已装 {count}";
|
|
43
43
|
readonly 'market.updatable': "可更新 {count}";
|
|
44
44
|
readonly 'market.deletedUpstream': "上游已删 {count}";
|
|
@@ -103,6 +103,9 @@ export declare const zh: {
|
|
|
103
103
|
readonly 'groups.empty': "还没有场景。先新建一个,再把技能归类。";
|
|
104
104
|
readonly 'groups.noCollections': "暂无来源组。从市场导入技能后自动按来源聚合。";
|
|
105
105
|
readonly 'groups.personal': "个人";
|
|
106
|
+
readonly 'groups.project': "项目级";
|
|
107
|
+
readonly 'groups.subdivide': "细分";
|
|
108
|
+
readonly 'groups.merge': "合并";
|
|
106
109
|
readonly 'groups.noWritable': "该组没有可写技能(只读技能无法由中枢开关)";
|
|
107
110
|
readonly 'groups.closeAll': "全部关闭";
|
|
108
111
|
readonly 'groups.keepOn': "保留开启";
|
|
@@ -129,6 +132,7 @@ export declare const zh: {
|
|
|
129
132
|
readonly 'row.disable': "禁用";
|
|
130
133
|
readonly 'row.enable': "启用";
|
|
131
134
|
readonly 'row.delete': "移入回收站";
|
|
135
|
+
readonly 'row.open': "查看 {name} 详情(回车)";
|
|
132
136
|
readonly 'delete.confirmTitle': "移入回收站?";
|
|
133
137
|
readonly 'delete.confirmText': "确定把「{name}」移入回收站吗?可随时恢复。";
|
|
134
138
|
readonly 'delete.confirm': "移入回收站";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Built-in market catalog: well-known skill repos shown on the market tab so
|
|
3
|
-
* users can browse and install without knowing repo URLs
|
|
3
|
+
* users can browse and install without knowing repo URLs.
|
|
4
4
|
* Only the repo slug lives here; adding goes through the normal
|
|
5
5
|
* market-source route and records upstream tracking like any manual source.
|
|
6
6
|
*/
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Market tab:
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* the importable
|
|
2
|
+
* Market tab: one unified market list — built-in catalog entries (add
|
|
3
|
+
* button while not yet added) and the user's market sources (full state
|
|
4
|
+
* badges + actions once added, plus custom sources), with check-all and
|
|
5
|
+
* update-all actions and the repo scan result with the importable checklist.
|
|
6
6
|
*/
|
|
7
|
-
import type
|
|
7
|
+
import { type JSX } from 'react';
|
|
8
8
|
import type { SkillHubState } from './useSkillHub.ts';
|
|
9
9
|
export declare function MarketView(props: {
|
|
10
10
|
hub: SkillHubState;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The skill hub panel: catalog grouped by tags + source collections, search
|
|
3
3
|
* and filter in one row, per-group tri-state switches with conflict dialogs,
|
|
4
4
|
* upstream source tracking (check / sync / follow upstream deletion into a
|
|
5
|
-
* restorable trash),
|
|
5
|
+
* restorable trash), market sources, disabled re-enable,
|
|
6
6
|
* detail inspection, and the new-skill scaffold form.
|
|
7
7
|
*
|
|
8
8
|
* Thin shell: state and flows live in useSkillHub, the tab contents live in
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Sources tab: the flat skill list or the grouped view
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Sources tab: the flat skill list or the grouped view — a project-level
|
|
3
|
+
* three-tier tree (workspaces from workspace.json, each optionally split by
|
|
4
|
+
* .dsh/.agents), one card per upstream collection with check/sync/
|
|
5
|
+
* follow-delete actions and the tri-state switch, plus the uncategorized
|
|
6
|
+
* "personal" card (project skills never count as personal).
|
|
5
7
|
*/
|
|
6
8
|
import type { JSX } from 'react';
|
|
7
9
|
import type { SkillHubState } from './useSkillHub.ts';
|
|
@@ -53,6 +53,7 @@ export declare function useSkillHub(api: SkillHubApi): {
|
|
|
53
53
|
repoImporting: boolean;
|
|
54
54
|
repoResult: RepoImportResponse | null;
|
|
55
55
|
search: string;
|
|
56
|
+
workspace: string;
|
|
56
57
|
detail: SkillDetail | null;
|
|
57
58
|
detailLoading: boolean;
|
|
58
59
|
busyNames: ReadonlySet<string>;
|
|
@@ -108,6 +109,7 @@ export declare function useSkillHub(api: SkillHubApi): {
|
|
|
108
109
|
tagBusy: boolean;
|
|
109
110
|
editSearch: string;
|
|
110
111
|
collapsedGroups: ReadonlySet<string>;
|
|
112
|
+
subdividedProjects: ReadonlySet<string>;
|
|
111
113
|
showLegend: boolean;
|
|
112
114
|
actionNames: Set<string>;
|
|
113
115
|
viewNames: Set<string>;
|
|
@@ -119,6 +121,7 @@ export declare function useSkillHub(api: SkillHubApi): {
|
|
|
119
121
|
setLoadError: import("react").Dispatch<import("react").SetStateAction<string | null>>;
|
|
120
122
|
setSuccessBanner: import("react").Dispatch<import("react").SetStateAction<string | null>>;
|
|
121
123
|
setSearch: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
124
|
+
setWorkspace: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
122
125
|
setDetail: import("react").Dispatch<import("react").SetStateAction<SkillDetail | null>>;
|
|
123
126
|
setShowForm: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
124
127
|
setFormName: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
@@ -148,6 +151,7 @@ export declare function useSkillHub(api: SkillHubApi): {
|
|
|
148
151
|
setEditSearch: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
149
152
|
setShowLegend: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
150
153
|
toggleGroupCollapse: (key: string) => void;
|
|
154
|
+
toggleSubdivide: (key: string) => void;
|
|
151
155
|
checkUpdate: () => Promise<void>;
|
|
152
156
|
loadMarket: () => Promise<void>;
|
|
153
157
|
openDetail: (name: string) => Promise<void>;
|
|
@@ -6,15 +6,16 @@
|
|
|
6
6
|
* plugin should show no trace of it.
|
|
7
7
|
*/
|
|
8
8
|
import { type ReactElement } from 'react';
|
|
9
|
+
import type { HubKey } from './locales.ts';
|
|
9
10
|
import type { CardShell } from './settings-form.ts';
|
|
10
11
|
/** Card-level chrome props. */
|
|
11
12
|
export interface PluginSettingsCardProps {
|
|
12
|
-
/** Locale translator for the owning plugin's namespace. */
|
|
13
|
-
t: (key:
|
|
13
|
+
/** Locale translator for the owning plugin's namespace (its own key domain). */
|
|
14
|
+
t: (key: HubKey) => string;
|
|
14
15
|
/** Locale key of the card title. */
|
|
15
|
-
titleKey:
|
|
16
|
+
titleKey: HubKey;
|
|
16
17
|
/** Locale key of the card description. */
|
|
17
|
-
descriptionKey:
|
|
18
|
+
descriptionKey: HubKey;
|
|
18
19
|
/** The form shell state. */
|
|
19
20
|
state: CardShell;
|
|
20
21
|
onSave: () => void;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Context } from '@deepseek-ai/cordis';
|
|
10
10
|
import z from 'schemastery';
|
|
11
|
+
import { type HubSettingsValue } from './protocol.ts';
|
|
11
12
|
/** Stable cordis plugin name (matches cordis.patch.yml insert id). */
|
|
12
13
|
export declare const name = "skill-hub";
|
|
13
14
|
/** Services required before the skill-hub surfaces can mount. */
|
|
@@ -26,11 +27,21 @@ export interface Config {
|
|
|
26
27
|
showGroupSummary?: boolean;
|
|
27
28
|
}
|
|
28
29
|
export declare const Config: z<Config>;
|
|
30
|
+
/**
|
|
31
|
+
* Settings namespace hosting the hub's runtime config. Since dsh rc.7 the
|
|
32
|
+
* host serves every registered settings namespace to the web client (the
|
|
33
|
+
* dsh-host-apiproxy allowlist is gone), so the browser card and the settings
|
|
34
|
+
* page edit this namespace through the official settings transport, and the
|
|
35
|
+
* plugin consumes the same resolved value — one source of truth.
|
|
36
|
+
*/
|
|
37
|
+
export declare const CONFIG_NAMESPACE: import("@deepseek-ai/dsh-settings").SettingsNamespace;
|
|
38
|
+
/** Schema of the hub's settings namespace: the card's fields (booleans + optional dot colors). */
|
|
39
|
+
export declare const HubSettingsSchema: z<HubSettingsValue>;
|
|
29
40
|
/** Model-facing announcement: plugin presence, capabilities, and limits. */
|
|
30
41
|
export declare const SKILL_HUB_GUIDANCE: string;
|
|
31
42
|
/**
|
|
32
43
|
* Mount the skill hub routes and announcement.
|
|
33
|
-
* @param ctx - host plugin context carrying webServer/skills/systemPrompt.
|
|
44
|
+
* @param ctx - host plugin context carrying webServer/skills/systemPrompt/settings.
|
|
34
45
|
* @param config - resolved plugin config (schema defaults applied by the loader).
|
|
35
46
|
*/
|
|
36
47
|
export declare function apply(ctx: Context, config?: Config): void;
|
package/lib/types/protocol.d.ts
CHANGED
|
@@ -52,10 +52,16 @@ export interface CatalogSkill {
|
|
|
52
52
|
provider: string;
|
|
53
53
|
/** Whether the hub may toggle this skill (user-level filesystem skills only). */
|
|
54
54
|
writable: boolean;
|
|
55
|
+
/** 技能来源标识(user-dsh/user-agents/project-dsh/project-agents/...)。 */
|
|
56
|
+
source: string;
|
|
55
57
|
/** SKILL.md creation time (epoch ms); used for "added" sorting. Absent when unknown. */
|
|
56
58
|
addedAt?: number;
|
|
57
59
|
/** SKILL.md last-modified time (epoch ms); used for "updated" display. Absent when unknown. */
|
|
58
60
|
updatedAt?: number;
|
|
61
|
+
/** 项目技能的所属工作区路径(仅 project-dsh/project-agents 来源携带)。 */
|
|
62
|
+
workspace?: string;
|
|
63
|
+
/** 工作区显示标题(来自 workspace.json;无则回退为路径)。 */
|
|
64
|
+
workspaceTitle?: string;
|
|
59
65
|
}
|
|
60
66
|
/** One disabled skill tracked by the hub sidecar (SKILL.md renamed away). */
|
|
61
67
|
export interface DisabledSkill {
|
|
@@ -194,6 +200,23 @@ export interface HubConfig {
|
|
|
194
200
|
/** Show group-header usage summaries (count + last used). Default true. */
|
|
195
201
|
showGroupSummary?: boolean;
|
|
196
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* The resolved shape of the hub's settings namespace (schema defaults, then
|
|
205
|
+
* the composition base, then the user layer). Kept as a type alias so the
|
|
206
|
+
* browser-side settings scope snapshot is index-compatible with the card
|
|
207
|
+
* form's record shape.
|
|
208
|
+
*/
|
|
209
|
+
export type HubSettingsValue = {
|
|
210
|
+
enabled: boolean;
|
|
211
|
+
announceToAgent: boolean;
|
|
212
|
+
showUseCount: boolean;
|
|
213
|
+
showUseTime: boolean;
|
|
214
|
+
showGroupSummary: boolean;
|
|
215
|
+
/** Model-invocable dot color (#rrggbb); absent means the panel default. */
|
|
216
|
+
dotModelColor?: string;
|
|
217
|
+
/** User-invocable dot color (#rrggbb); absent means the panel default. */
|
|
218
|
+
dotUserColor?: string;
|
|
219
|
+
};
|
|
197
220
|
/**
|
|
198
221
|
* Hub config defaults — the single source every layer reads: the cordis
|
|
199
222
|
* schema (index.ts), the host's saved-override merge, and the routes'
|
package/lib/types/skillfs.d.ts
CHANGED
|
@@ -88,7 +88,7 @@ export declare function listSkillEntries(root: WritableRoot, home?: string): Pro
|
|
|
88
88
|
export declare function findProjectRoot(cwd: string): Promise<string>;
|
|
89
89
|
/**
|
|
90
90
|
* Scan one writable root for files the provider ignores, so the GUI can
|
|
91
|
-
* show why a skill never appears
|
|
91
|
+
* show why a skill never appears — a
|
|
92
92
|
* missing frontmatter must be visible, not silent). .disabled files belong
|
|
93
93
|
* to the hub and are skipped.
|
|
94
94
|
*/
|
package/lib/types/update.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Self-update check for dsh-skill-hub
|
|
2
|
+
* Self-update check for dsh-skill-hub
|
|
3
3
|
* query GitHub's latest release and compare it to the installed version.
|
|
4
4
|
*
|
|
5
5
|
* This stays dependency-free: Node's global fetch is used and the version
|