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.
- package/CONTRIBUTING.md +6 -4
- package/README.md +9 -5
- package/README.zh.md +9 -5
- package/lib/client.js +55 -58
- package/lib/client.js.map +1 -1
- package/lib/index.js +529 -315
- package/lib/types/client/SkillHubSettingsCard.d.ts +5 -3
- package/lib/types/client/icons.d.ts +4 -5
- package/lib/types/client/index.d.ts +13 -12
- package/lib/types/client/locales/market.d.ts +1 -0
- package/lib/types/client/locales.d.ts +1 -0
- package/lib/types/client/settings-form.d.ts +9 -3
- package/lib/types/client/slash-dots.d.ts +20 -11
- package/lib/types/index.d.ts +59 -24
- package/lib/types/protocol/api.d.ts +22 -2
- package/lib/types/protocol/config.d.ts +30 -4
- package/lib/types/protocol/market.d.ts +1 -1
- package/lib/types/protocol/repo.d.ts +18 -1
- package/lib/types/protocol.d.ts +3 -3
- package/lib/types/repo/discovery.d.ts +59 -3
- package/lib/types/repo/github-client.d.ts +16 -0
- package/lib/types/repo.d.ts +6 -3
- package/lib/types/routes/helpers.d.ts +8 -1
- package/lib/types/routes/market.d.ts +2 -2
- package/lib/types/routes.d.ts +1 -1
- package/lib/types/store/store.d.ts +4 -0
- package/package.json +31 -41
- package/src/client/SkillHubSettingsCard.tsx +5 -3
- package/src/client/icons.tsx +4 -5
- package/src/client/index.tsx +33 -32
- package/src/client/locales/market.ts +2 -0
- package/src/client/panel/RepoScanCard.tsx +6 -3
- package/src/client/panel/SkillHubPanel.tsx +1 -1
- package/src/client/panel/SourcesView.tsx +1 -1
- package/src/client/panel/hooks/useGroupFlow.ts +1 -8
- package/src/client/settings-form.ts +9 -4
- package/src/client/slash-dots.test.ts +19 -36
- package/src/client/slash-dots.tsx +30 -60
- package/src/index.ts +145 -82
- package/src/protocol/api.ts +24 -2
- package/src/protocol/config.ts +35 -4
- package/src/protocol/market.ts +1 -1
- package/src/protocol/repo.ts +22 -1
- package/src/protocol.ts +3 -3
- package/src/repo/discovery.ts +120 -33
- package/src/repo/github-client.ts +18 -1
- package/src/repo/install.ts +8 -7
- package/src/repo.test.ts +158 -4
- package/src/repo.ts +10 -2
- package/src/routes/config.ts +19 -2
- package/src/routes/helpers.ts +9 -2
- package/src/routes/market.ts +49 -37
- package/src/routes/repo-import.ts +3 -2
- package/src/routes/sources.ts +2 -1
- package/src/routes.test.ts +107 -1
- package/src/routes.ts +21 -3
- package/src/store/migrate.ts +3 -2
- package/src/store/store.ts +7 -2
- package/src/store.test.ts +50 -0
- package/src/update.ts +4 -2
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* The dsh-skill-hub plugin settings card: bridges the hub's settings
|
|
3
3
|
* namespace (bound through the official settings transport) onto the
|
|
4
4
|
* family-style staged card form (enabled master switch + agent announcement).
|
|
5
|
-
* Registered into
|
|
6
|
-
*
|
|
5
|
+
* Registered into `plugins.bundle.config` under the bundle's package name, so
|
|
6
|
+
* the Plugins manager renders it on dsh-skill-hub's own page. That slot is
|
|
7
|
+
* dispatched with `view: 'page'` only, so the card always renders its full
|
|
8
|
+
* form (no `summary` one-liner variant).
|
|
7
9
|
*/
|
|
8
10
|
import { type ReactElement } from 'react';
|
|
9
11
|
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
@@ -34,7 +36,7 @@ export interface SkillHubSettingsCardFace {
|
|
|
34
36
|
resetField: (field: string) => void;
|
|
35
37
|
}
|
|
36
38
|
/** Props the slot renderer binds (locale copy + injected form actions). */
|
|
37
|
-
export type SkillHubSettingsCardProps = PropsRuntime<'
|
|
39
|
+
export type SkillHubSettingsCardProps = PropsRuntime<'plugins.bundle.config'> & PropsLocale<'dsh-skill-hub'> & InjectFace<SkillHubSettingsCardFace>;
|
|
38
40
|
/** Bridges the hub's config scope onto the card's staged form. */
|
|
39
41
|
export declare class SkillHubSettingsCardController {
|
|
40
42
|
private readonly form;
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
* Vendored UI icons for dsh-skill-hub.
|
|
3
3
|
*
|
|
4
4
|
* These were originally imported from `@deepseek-ai/dsh-client-ui-primitives`.
|
|
5
|
-
* That package is
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* older and newer dsh hosts.
|
|
5
|
+
* That package is not exposed as a standalone plugin module by the dsh web
|
|
6
|
+
* build this plugin targets — dsh keeps a static compatibility module instead.
|
|
7
|
+
* Vendoring the few tiny outline icons the hub uses keeps the browser half
|
|
8
|
+
* self-contained and independent of how that module is packaged.
|
|
10
9
|
*
|
|
11
10
|
* SVG paths are copied verbatim from dsh-client-ui-primitives (MIT licensed)
|
|
12
11
|
* so the visuals remain pixel-identical to the dsh icon family.
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
* Browser-half entry for the dsh-skill-hub plugin — runs inside the dsh
|
|
3
3
|
* web GUI.
|
|
4
4
|
*
|
|
5
|
-
* Registers the dsh-skill-hub locale dictionaries and mounts
|
|
6
|
-
* surfaces, both through official slots (no DOM injection):
|
|
7
|
-
* - a plugin-management card in the `settings.plugin.item` slot (Settings →
|
|
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);
|
|
5
|
+
* Registers the dsh-skill-hub locale dictionaries and mounts:
|
|
13
6
|
* - a top-level Settings section (Settings → 技能) hosting the skill hub
|
|
14
|
-
* panel: catalog, search, enable/disable, diagnostics, new-skill form
|
|
7
|
+
* panel: catalog, search, enable/disable, diagnostics, new-skill form;
|
|
8
|
+
* - the chat "/" menu skill dots, colored from the same config the panel reads.
|
|
9
|
+
*
|
|
10
|
+
* - a configuration card in the `plugins.bundle.config` slot, keyed by the
|
|
11
|
+
* BUNDLE PACKAGE NAME, rendered on the plugin's own page in the Plugins
|
|
12
|
+
* manager (sidebar → 插件 → dsh-skill-hub). dsh 0.1.7 has no auto-generated
|
|
13
|
+
* config page: the manager only renders that section for bundles that
|
|
14
|
+
* register this slot, and the card writes through the shared config form
|
|
15
|
+
* (`ctx.configForms.get(skill-hub)`), the same form the host routes read.
|
|
15
16
|
*
|
|
16
17
|
* Failure policy: mounting problems are logged, never thrown — the web
|
|
17
18
|
* shell fails the whole boot when a plugin apply throws, and an external
|
|
@@ -37,9 +38,9 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
37
38
|
/**
|
|
38
39
|
* Required services (fiber inject waiting — the runtime must be up first).
|
|
39
40
|
* `connection`/`remote` are the settings transport's own prerequisites
|
|
40
|
-
* (`ctx.
|
|
41
|
-
* `
|
|
42
|
-
* settings-plugins inject list.
|
|
41
|
+
* (`ctx.configForms.get` resolves them on the caller's fiber), and
|
|
42
|
+
* `configForms` is the shared settings-form service itself; mirror the
|
|
43
|
+
* official settings-plugins inject list.
|
|
43
44
|
*/
|
|
44
45
|
export declare const inject: string[];
|
|
45
46
|
/** Type-only surface (export discipline: no value exports beyond the plugin contract). */
|
|
@@ -74,6 +74,7 @@ export declare const zhMarket: {
|
|
|
74
74
|
readonly 'repo.cancel': "取消";
|
|
75
75
|
readonly 'repo.cancelled': "已取消 · 已导入 {imported}/{total} 已保留,临时文件已清理";
|
|
76
76
|
readonly 'repo.importingCurrent': "正在导入: {name} ({done}/{total})";
|
|
77
|
+
readonly 'repo.root.self': "仓库根";
|
|
77
78
|
readonly 'repo.root.skills': "skills";
|
|
78
79
|
readonly 'repo.root.designTemplates': "design-templates";
|
|
79
80
|
readonly 'repo.root.generic': "{root}";
|
|
@@ -157,6 +157,7 @@ export declare const zh: {
|
|
|
157
157
|
readonly 'repo.cancel': "取消";
|
|
158
158
|
readonly 'repo.cancelled': "已取消 · 已导入 {imported}/{total} 已保留,临时文件已清理";
|
|
159
159
|
readonly 'repo.importingCurrent': "正在导入: {name} ({done}/{total})";
|
|
160
|
+
readonly 'repo.root.self': "仓库根";
|
|
160
161
|
readonly 'repo.root.skills': "skills";
|
|
161
162
|
readonly 'repo.root.designTemplates': "design-templates";
|
|
162
163
|
readonly 'repo.root.generic': "{root}";
|
|
@@ -54,7 +54,12 @@ export interface FieldState {
|
|
|
54
54
|
overridden: boolean;
|
|
55
55
|
invalid: boolean;
|
|
56
56
|
}
|
|
57
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* The settings-form face this card consumes. Declared structurally rather than
|
|
59
|
+
* imported so the card stays independent of the settings package's generics:
|
|
60
|
+
* the browser half passes it `ctx.configForms.get<HubSettingsValue>(HUB_ENTRY_ID)`,
|
|
61
|
+
* which satisfies this shape (ConfigForm<T>).
|
|
62
|
+
*/
|
|
58
63
|
export interface FormScope {
|
|
59
64
|
subscribe(listener: () => void): () => void;
|
|
60
65
|
getSnapshot(): {
|
|
@@ -64,8 +69,9 @@ export interface FormScope {
|
|
|
64
69
|
base?: unknown;
|
|
65
70
|
user?: unknown;
|
|
66
71
|
};
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
/** Both write paths report host acceptance (ConfigForm.set/unset contract). */
|
|
73
|
+
set(field: string, value: unknown): Promise<boolean>;
|
|
74
|
+
unset(field: string): Promise<boolean>;
|
|
69
75
|
}
|
|
70
76
|
/** Stages one card's edits and writes them through the settings transport. */
|
|
71
77
|
export declare class CardForm {
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
* Slash-menu skill dots: puts the invocation-status dot (model-callable blue /
|
|
3
3
|
* user-only green) in front of every skill candidate in the chat `/` menu.
|
|
4
4
|
*
|
|
5
|
-
* Mechanism
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* the
|
|
5
|
+
* Mechanism: hooks the `/skill` source's `candidates` so the hub learns which
|
|
6
|
+
* rows are skill candidates and how each one classifies for model invocation,
|
|
7
|
+
* then injects the dot straight into the rendered option rows. The menu's
|
|
8
|
+
* `icon` slot is unusable for this — `MenuView` narrowed `icon` to an enum
|
|
9
|
+
* ('file' | 'folder' | 'session') and renders it through `ReferenceIcon`, so a
|
|
10
|
+
* custom element never reaches the DOM. Colors come from the same settings
|
|
11
|
+
* (dotModelColor / dotUserColor) and the same `modelInvocable` classification
|
|
12
|
+
* the panel legend uses — so the chat menu and the Settings → 技能 panel stay
|
|
13
|
+
* in sync, and editing the color updates both.
|
|
13
14
|
*
|
|
14
15
|
* The skill source is registered by the core plugin under the `name` "skill"
|
|
15
16
|
* on the `/` trigger; re-registering the same name would throw, so this wraps
|
|
@@ -18,7 +19,7 @@
|
|
|
18
19
|
* / `sessionOf` — the lookup below is defensive: it never throws).
|
|
19
20
|
*/
|
|
20
21
|
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
21
|
-
import type {
|
|
22
|
+
import type { ConfigForm } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
22
23
|
import type { InputTriggerServiceContract, InputTriggerSource } from '@deepseek-ai/dsh-client-ui-input-trigger/client';
|
|
23
24
|
import type { HubSettingsValue } from '../protocol.ts';
|
|
24
25
|
import type { SkillHubApi } from './api.ts';
|
|
@@ -35,7 +36,15 @@ export declare function findSkillSource(service: InputTriggerServiceContract): I
|
|
|
35
36
|
* catalog wins after reconnect; exported for deterministic unit tests.
|
|
36
37
|
*/
|
|
37
38
|
export declare function resetModelCache(): void;
|
|
38
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Wrap the core skill source so every menu row carries the invocation dot.
|
|
41
|
+
* Exported for unit tests; production wiring goes through setupSkillSlashDots.
|
|
42
|
+
* @param source - the registered `/skill` source.
|
|
43
|
+
* @param api - hub browser API for the modelInvocable lookup.
|
|
44
|
+
* @param scope - hub settings scope for the dot colors.
|
|
45
|
+
* @returns a disposer restoring the original candidates.
|
|
46
|
+
*/
|
|
47
|
+
export declare function wrapSkillSource(source: InputTriggerSource, api: SkillHubApi, scope: ConfigForm<HubSettingsValue>): () => void;
|
|
39
48
|
/**
|
|
40
49
|
* Mount the slash-menu dots on the registered `/skill` source. Idempotent and
|
|
41
50
|
* defensive: if the core source isn't registered yet (or the registry shape
|
|
@@ -47,4 +56,4 @@ export declare function wrapSkillSource(source: InputTriggerSource, api: SkillHu
|
|
|
47
56
|
* @param scope - hub settings scope for dot colors.
|
|
48
57
|
* @returns a cleanup function for `ctx.effect`.
|
|
49
58
|
*/
|
|
50
|
-
export declare function setupSkillSlashDots(ctx: ClientContext, api: SkillHubApi, scope:
|
|
59
|
+
export declare function setupSkillSlashDots(ctx: ClientContext, api: SkillHubApi, scope: ConfigForm<HubSettingsValue>): () => void;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -6,47 +6,82 @@
|
|
|
6
6
|
* the skill hub panel. Everything rides official NPM SDK packages — no dsh
|
|
7
7
|
* source changes.
|
|
8
8
|
*/
|
|
9
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
9
|
+
import type { Context, Volatile } from '@deepseek-ai/cordis';
|
|
10
10
|
import type { SettingsNamespace } from '@deepseek-ai/dsh-settings';
|
|
11
|
-
import z from 'schemastery';
|
|
12
|
-
import { type HubSettingsValue } from './protocol.ts';
|
|
11
|
+
import z from '@deepseek-ai/schemastery';
|
|
13
12
|
/** Stable cordis plugin name (matches cordis.patch.yml insert id). */
|
|
14
13
|
export declare const name = "skill-hub";
|
|
15
|
-
/** Services required before the skill-hub surfaces can mount. */
|
|
14
|
+
/** Services required before the skill-hub surfaces can mount. `settings` is deliberately NOT here: the plugin reads its own volatile config refs and only reaches for the settings service when it is present (issue #11). */
|
|
16
15
|
export declare const inject: string[];
|
|
17
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Plugin config, validated by the same-named schemastery schema.
|
|
18
|
+
*
|
|
19
|
+
* dsh 0.1.7 replaced the "plugin registers a settings namespace" model with
|
|
20
|
+
* "the Loader entry's Config IS the settings namespace", so this one schema is
|
|
21
|
+
* both the composition config and the settings page. Every field is volatile:
|
|
22
|
+
* the Loader re-resolves volatile values in place and re-enters apply()
|
|
23
|
+
* instead of rebuilding the fiber, and only volatile fields are writable
|
|
24
|
+
* through the settings transport.
|
|
25
|
+
*/
|
|
18
26
|
export interface Config {
|
|
19
27
|
/** When true (default), a system-prompt section announces the hub to every agent. */
|
|
20
|
-
announceToAgent
|
|
28
|
+
announceToAgent: Volatile<boolean>;
|
|
21
29
|
/** Master switch for the plugin (routes, prompt section). */
|
|
22
|
-
enabled
|
|
30
|
+
enabled: Volatile<boolean>;
|
|
23
31
|
/** Show per-skill invocation count chip. Default true. */
|
|
24
|
-
showUseCount
|
|
32
|
+
showUseCount: Volatile<boolean>;
|
|
25
33
|
/** Show per-skill last-used relative time. Default true. */
|
|
26
|
-
showUseTime
|
|
34
|
+
showUseTime: Volatile<boolean>;
|
|
27
35
|
/** Show group-header usage summaries (count + last used). Default true. */
|
|
28
|
-
showGroupSummary
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
|
|
36
|
+
showGroupSummary: Volatile<boolean>;
|
|
37
|
+
/** 模型可调圆点颜色(#rrggbb);缺省用面板默认色。 */
|
|
38
|
+
dotModelColor: Volatile<string | undefined>;
|
|
39
|
+
/** 用户可调圆点颜色(#rrggbb);缺省用面板默认色。 */
|
|
40
|
+
dotUserColor: Volatile<string | undefined>;
|
|
41
|
+
/** GitHub token;`role('secret')` 让 settings 层统一脱敏,缺省为匿名。 */
|
|
42
|
+
githubToken: Volatile<string | undefined>;
|
|
43
|
+
/** 统计滚动窗口天数:只统计最近 N 天的使用;0 = 全部历史。 */
|
|
44
|
+
statsWindowDays: Volatile<number>;
|
|
45
|
+
/** 自动统计扫描间隔(分钟,最小 1)。 */
|
|
46
|
+
statsScanMinutes: Volatile<number>;
|
|
33
47
|
}
|
|
34
|
-
export declare const Config: z<Config>;
|
|
35
48
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
* The live plugin config the settings page edits. Field order here is the row
|
|
50
|
+
* order the auto-generated page renders.
|
|
51
|
+
*/
|
|
52
|
+
export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
53
|
+
enabled: z<boolean, boolean, "volatile-defined">;
|
|
54
|
+
announceToAgent: z<boolean, boolean, "volatile-defined">;
|
|
55
|
+
dotModelColor: z<string, string, "volatile">;
|
|
56
|
+
dotUserColor: z<string, string, "volatile">;
|
|
57
|
+
showUseCount: z<boolean, boolean, "volatile-defined">;
|
|
58
|
+
showUseTime: z<boolean, boolean, "volatile-defined">;
|
|
59
|
+
showGroupSummary: z<boolean, boolean, "volatile-defined">;
|
|
60
|
+
statsWindowDays: z<number, number, "volatile-defined">;
|
|
61
|
+
statsScanMinutes: z<number, number, "volatile-defined">;
|
|
62
|
+
githubToken: z<string, string, "volatile">;
|
|
63
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
64
|
+
enabled: z<boolean, boolean, "volatile-defined">;
|
|
65
|
+
announceToAgent: z<boolean, boolean, "volatile-defined">;
|
|
66
|
+
dotModelColor: z<string, string, "volatile">;
|
|
67
|
+
dotUserColor: z<string, string, "volatile">;
|
|
68
|
+
showUseCount: z<boolean, boolean, "volatile-defined">;
|
|
69
|
+
showUseTime: z<boolean, boolean, "volatile-defined">;
|
|
70
|
+
showGroupSummary: z<boolean, boolean, "volatile-defined">;
|
|
71
|
+
statsWindowDays: z<number, number, "volatile-defined">;
|
|
72
|
+
statsScanMinutes: z<number, number, "volatile-defined">;
|
|
73
|
+
githubToken: z<string, string, "volatile">;
|
|
74
|
+
}>>, "plain">;
|
|
75
|
+
/**
|
|
76
|
+
* The settings namespace this plugin's config lives under, narrowed to the
|
|
77
|
+
* settings package's branded type. The browser half resolves the same form
|
|
78
|
+
* through `ctx.configForms.get(HUB_ENTRY_ID)`.
|
|
41
79
|
*/
|
|
42
|
-
export declare const
|
|
43
|
-
/** Schema of the hub's settings namespace: the card's fields (booleans + optional dot colors). */
|
|
44
|
-
export declare const HubSettingsSchema: z<HubSettingsValue>;
|
|
80
|
+
export declare const ENTRY_ID: SettingsNamespace;
|
|
45
81
|
/** Model-facing announcement: plugin presence, capabilities, and limits. */
|
|
46
82
|
export declare const SKILL_HUB_GUIDANCE: string;
|
|
47
83
|
/**
|
|
48
84
|
* Mount the skill hub routes and announcement.
|
|
49
85
|
* @param ctx - host plugin context carrying webServer/skills/systemPrompt/settings.
|
|
50
|
-
* @param config - resolved plugin config (schema defaults applied by the loader).
|
|
51
86
|
*/
|
|
52
87
|
export declare function apply(ctx: Context, config?: Config): void;
|
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Root path of the skill-hub API family. The host also registers this as its
|
|
3
|
+
* 404 catch-all prefix, so a mistyped path answers with a plain 404 naming the
|
|
4
|
+
* path instead of falling through to the SPA fallback (which answers 401 and
|
|
5
|
+
* reads like an auth problem).
|
|
6
|
+
*/
|
|
7
|
+
export declare const SKILL_HUB_API_ROOT = "/api/skill-hub";
|
|
8
|
+
/**
|
|
9
|
+
* Deprecated path of the market update check, kept routable for one release
|
|
10
|
+
* after the naming unification below. A browser tab that loaded the previous
|
|
11
|
+
* client bundle keeps calling it until it reloads; delete this (and its route
|
|
12
|
+
* in routes/market.ts) in the next minor.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SKILL_HUB_API_DEPRECATED_MARKET_CHECK = "/api/skill-hub/market/check";
|
|
15
|
+
/**
|
|
16
|
+
* Browser-facing base paths of the skill-hub API family.
|
|
17
|
+
*
|
|
18
|
+
* Naming rule: a path's segments mirror its scope. Market sources own the
|
|
19
|
+
* `/market/source/*` subtree — add, delete, ref, versions, check, sync — so
|
|
20
|
+
* the update check and the sync that acts on its result sit side by side.
|
|
21
|
+
*/
|
|
2
22
|
export declare const SKILL_HUB_API: {
|
|
3
23
|
readonly catalog: "/api/skill-hub/catalog";
|
|
4
24
|
readonly skill: "/api/skill-hub/skill";
|
|
@@ -12,7 +32,7 @@ export declare const SKILL_HUB_API: {
|
|
|
12
32
|
readonly marketSource: "/api/skill-hub/market/source";
|
|
13
33
|
readonly marketSourceDelete: "/api/skill-hub/market/source/delete";
|
|
14
34
|
readonly marketSourceRef: "/api/skill-hub/market/source/ref";
|
|
15
|
-
readonly marketCheck: "/api/skill-hub/market/check";
|
|
35
|
+
readonly marketCheck: "/api/skill-hub/market/source/check";
|
|
16
36
|
readonly marketSync: "/api/skill-hub/market/source/sync";
|
|
17
37
|
readonly repo: "/api/skill-hub/repo";
|
|
18
38
|
readonly repoImport: "/api/skill-hub/repo/import";
|
|
@@ -29,6 +29,14 @@ export interface HubConfig {
|
|
|
29
29
|
*/
|
|
30
30
|
githubToken?: string;
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* HubConfig minus the GitHub token: the shape of every config payload that
|
|
34
|
+
* leaves the host over HTTP. The token is write-only there — a response
|
|
35
|
+
* pasted into an issue or a screenshot would otherwise hand the credential
|
|
36
|
+
* over, so callers read `ConfigResponse.githubTokenSet` when they only need
|
|
37
|
+
* to know whether one is in effect.
|
|
38
|
+
*/
|
|
39
|
+
export type RedactedHubConfig = Omit<HubConfig, 'githubToken'>;
|
|
32
40
|
/**
|
|
33
41
|
* The resolved shape of the hub's settings namespace (schema defaults, then
|
|
34
42
|
* the composition base, then the user layer). Kept as a type alias so the
|
|
@@ -52,6 +60,14 @@ export type HubSettingsValue = {
|
|
|
52
60
|
/** GitHub token(明文存本地设置文档,仅回环可读;缺省为匿名)。 */
|
|
53
61
|
githubToken?: string;
|
|
54
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* This plugin's Loader entry id — the settings namespace dsh serves its config
|
|
65
|
+
* under. It is cordis.patch.yml's insert id, NOT the package name, and it is
|
|
66
|
+
* shared by contract because the browser half addresses the same form through
|
|
67
|
+
* `ctx.configForms.get(HUB_ENTRY_ID)` while the host half writes through
|
|
68
|
+
* `ctx.settings.update(HUB_ENTRY_ID, …)`.
|
|
69
|
+
*/
|
|
70
|
+
export declare const HUB_ENTRY_ID = "skill-hub";
|
|
55
71
|
/**
|
|
56
72
|
* Hub config defaults — the single source every layer reads: the cordis
|
|
57
73
|
* schema (index.ts), the host's saved-override merge, and the routes'
|
|
@@ -74,6 +90,14 @@ export declare const HUB_CONFIG_DEFAULTS: {
|
|
|
74
90
|
* their sane ranges (window ≥ 0, scan interval ≥ 1 minute).
|
|
75
91
|
*/
|
|
76
92
|
export declare function resolveHubConfig(saved: Partial<HubConfig>, base?: Partial<HubConfig>): HubConfig;
|
|
93
|
+
/**
|
|
94
|
+
* Drop the GitHub token from a config-shaped object. Returns a copy, so the
|
|
95
|
+
* caller's own config layer keeps the token. Shared by the config route's GET
|
|
96
|
+
* and POST responses — neither may echo it back.
|
|
97
|
+
*/
|
|
98
|
+
export declare function redactGithubToken<T extends {
|
|
99
|
+
githubToken?: string;
|
|
100
|
+
}>(value: T): Omit<T, 'githubToken'>;
|
|
77
101
|
/** HEX color validation shared by host routes and the settings card. */
|
|
78
102
|
export declare const HEX_COLOR_RE: RegExp;
|
|
79
103
|
/** GET /api/skill-hub/config */
|
|
@@ -81,10 +105,12 @@ export interface ConfigResponse {
|
|
|
81
105
|
ok: true;
|
|
82
106
|
/** 已安装插件自身的版本号(package.json version),设置卡标题旁显示。 */
|
|
83
107
|
pluginVersion: string;
|
|
84
|
-
/** Effective configuration (saved overrides merged over the defaults). */
|
|
85
|
-
config:
|
|
86
|
-
/** Raw user overrides persisted in the sidecar (absent fields inherit defaults). */
|
|
87
|
-
saved: Partial<
|
|
108
|
+
/** Effective configuration (saved overrides merged over the defaults), token stripped. */
|
|
109
|
+
config: RedactedHubConfig;
|
|
110
|
+
/** Raw user overrides persisted in the sidecar (absent fields inherit defaults), token stripped. */
|
|
111
|
+
saved: Partial<RedactedHubConfig>;
|
|
112
|
+
/** True when a GitHub token is in effect (saved override or the base/env layer). */
|
|
113
|
+
githubTokenSet: boolean;
|
|
88
114
|
}
|
|
89
115
|
/** POST /api/skill-hub/config — a partial patch; omitted fields keep their values. */
|
|
90
116
|
export interface ConfigRequest {
|
|
@@ -64,7 +64,7 @@ export interface MarketStatsResponse {
|
|
|
64
64
|
error?: string;
|
|
65
65
|
}>;
|
|
66
66
|
}
|
|
67
|
-
/** GET /api/skill-hub/market/check — update check over market sources. */
|
|
67
|
+
/** GET /api/skill-hub/market/source/check — update check over market sources. */
|
|
68
68
|
export interface MarketCheckResponse {
|
|
69
69
|
ok: true;
|
|
70
70
|
results: Array<{
|
|
@@ -17,8 +17,25 @@ export interface RepoSkillEntry {
|
|
|
17
17
|
/** Whether a same-named skill already exists locally. */
|
|
18
18
|
existing: boolean;
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Skill root in a GitHub repo: the top-level directory that contains skills
|
|
22
|
+
* (e.g. skills, design-templates, templates). Auto-derived from SKILL.md
|
|
23
|
+
* locations, not hard-coded.
|
|
24
|
+
*
|
|
25
|
+
* The empty string is the repo root itself — a skill whose SKILL.md sits at
|
|
26
|
+
* the top of the tree (a Claude Code plugin manifest may declare
|
|
27
|
+
* `"skills": ["./"]`). It is a legal value, not a missing one, so state
|
|
28
|
+
* sanitizers must accept it instead of coercing it to a default root.
|
|
29
|
+
*/
|
|
21
30
|
export type RepoRoot = string;
|
|
31
|
+
/** Top-level directory pattern for a skill root: visible, non-dot, safe chars. First char must be alphanum. */
|
|
32
|
+
export declare const REPO_ROOT_RE: RegExp;
|
|
33
|
+
/**
|
|
34
|
+
* True when a value is a usable root: a visible safe top-level directory name,
|
|
35
|
+
* or the empty string for the repo root. Guards the persisted store against
|
|
36
|
+
* corrupt roots without rewriting the repo-root sentinel.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isValidRepoRoot(value: unknown): value is RepoRoot;
|
|
22
39
|
/** GET /api/skill-hub/repo — discover importable skills in a GitHub repo. */
|
|
23
40
|
export interface RepoDiscoverResponse {
|
|
24
41
|
ok: true;
|
package/lib/types/protocol.d.ts
CHANGED
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
* 按域拆分后的重导出入口:老 `from './protocol.ts'` 写法保持可用,
|
|
8
8
|
* 新代码可按需直引 `from './protocol/<domain>.ts'`。
|
|
9
9
|
*/
|
|
10
|
-
export { SKILL_HUB_API } from './protocol/api.ts';
|
|
10
|
+
export { SKILL_HUB_API, SKILL_HUB_API_ROOT, SKILL_HUB_API_DEPRECATED_MARKET_CHECK } from './protocol/api.ts';
|
|
11
11
|
export type { WritableRoot, HubInvocation, CatalogSkill, DisabledSkill, DiagnosticEntry, DiagnosticFixRequest, DiagnosticFixResponse, CatalogResponse, SkillDetail, SkillDetailResponse, SkillDeleteRequest, SkillDeleteResponse, ToggleRequest, ToggleResponse, ToggleBatchRequest, ToggleBatchResponse, CreateRequest, CreateResponse, } from './protocol/catalog.ts';
|
|
12
12
|
export type { SkillStat, StatsResponse, SkillStatsCheckpoint } from './protocol/stats.ts';
|
|
13
|
-
export type { ErrorResponse, HubConfig, HubSettingsValue, ConfigResponse, ConfigRequest } from './protocol/config.ts';
|
|
14
|
-
export { HUB_CONFIG_DEFAULTS, HEX_COLOR_RE, GITHUB_TOKEN_RE, resolveHubConfig } from './protocol/config.ts';
|
|
13
|
+
export type { ErrorResponse, HubConfig, HubSettingsValue, RedactedHubConfig, ConfigResponse, ConfigRequest } from './protocol/config.ts';
|
|
14
|
+
export { HUB_CONFIG_DEFAULTS, HUB_ENTRY_ID, HEX_COLOR_RE, GITHUB_TOKEN_RE, resolveHubConfig, redactGithubToken } from './protocol/config.ts';
|
|
15
15
|
export type { MarketSourceRecord, MarketSourcesResponse, MarketSourceRequest, MarketSourceResponse, MarketSourceRefRequest, MarketSourceVersionsResponse, MarketStatsSnapshot, MarketStatsResponse, MarketCheckResponse, MarketSyncResponse, } from './protocol/market.ts';
|
|
16
16
|
export type { RepoSkillEntry, RepoRoot, RepoDiscoverResponse, RepoImportRequest, RepoImportResponse, RepoImportProgressResponse, RepoImportCancelRequest, RepoImportCancelResponse, UpdateCheckResponse, } from './protocol/repo.ts';
|
|
17
17
|
export type { SkillTag, CollectionGroup, GroupsResponse, TagSaveRequest, TagSaveResponse, TagDeleteRequest, TagDeleteResponse, TagMembersRequest, TagMembersResponse, TagReorderRequest, TagReorderResponse, CollectionReorderRequest, CollectionReorderResponse, SourceGroupReorderRequest, SourceGroupReorderResponse, } from './protocol/groups.ts';
|
|
@@ -2,9 +2,48 @@
|
|
|
2
2
|
* 仓库技能发现:从 GitHub 仓库树推断技能根目录、列出可导入技能、
|
|
3
3
|
* 生成目录清单并做上游差异比对。纯函数为主,便于单测。
|
|
4
4
|
* 从 repo.ts 抽出。
|
|
5
|
+
*
|
|
6
|
+
* 路径语义集中在几个内部函数上:`skillDirPrefix`(技能目录在树里的前缀)、
|
|
7
|
+
* `isRepoTooling`(永不属于技能的仓库基建)、`skillFileAt`(目录内的
|
|
8
|
+
* SKILL.md 路径)。collect / manifest / diff 三处必须共用它们,否则差异
|
|
9
|
+
* 比对会永远报「有更新」。
|
|
5
10
|
*/
|
|
6
|
-
import type { RepoSkillEntry } from '../protocol.ts';
|
|
11
|
+
import type { RepoRoot, RepoSkillEntry } from '../protocol.ts';
|
|
7
12
|
import type { RepoFile, RepoRef, RepoTreeItem } from './types.ts';
|
|
13
|
+
/**
|
|
14
|
+
* Sentinel root for a skill whose SKILL.md sits directly at the repository
|
|
15
|
+
* root instead of under a top-level directory. That layout is legal and used
|
|
16
|
+
* upstream (a Claude Code plugin manifest may declare `"skills": ["./"]`),
|
|
17
|
+
* and the empty string is the representation that composes with plain prefix
|
|
18
|
+
* arithmetic: an empty prefix *is* the repo root. Every path helper below
|
|
19
|
+
* special-cases it, because `'' + '/' + name` would produce a bogus absolute
|
|
20
|
+
* `/name` that matches no tree path.
|
|
21
|
+
*/
|
|
22
|
+
export declare const REPO_ROOT: RepoRoot;
|
|
23
|
+
/** True when a root denotes the repo root itself (the repo is the skill dir). */
|
|
24
|
+
export declare function isRepoRoot(root: string): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The tree prefix delimiting a skill directory: empty for the repo-root skill
|
|
27
|
+
* (it owns the whole tree), otherwise `<dir>/`. Single source for collect,
|
|
28
|
+
* manifest, diff and the store's baseline cleanup, so they can never disagree.
|
|
29
|
+
*/
|
|
30
|
+
export declare function skillDirPrefix(dir: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* A path inside a skill directory, as it appears in the repo tree. Both
|
|
33
|
+
* directions of this mapping matter: repo paths are always '/'-joined (never
|
|
34
|
+
* node:path's OS separator, these are GitHub paths shown in the UI), and the
|
|
35
|
+
* repo-root case must not grow a leading slash.
|
|
36
|
+
*/
|
|
37
|
+
export declare function skillPathIn(dir: string, relative: string): string;
|
|
38
|
+
/** The SKILL.md path inside a skill directory. */
|
|
39
|
+
export declare function skillFileAt(dir: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* A repo file's path relative to its skill directory — the path it is written
|
|
42
|
+
* to under the skill's own directory. `dir === ''` means the repo file path
|
|
43
|
+
* *is* the relative path; slicing `dir.length + 1` would eat its first
|
|
44
|
+
* character.
|
|
45
|
+
*/
|
|
46
|
+
export declare function relativeToSkillDir(dir: string, path: string): string;
|
|
8
47
|
/** `owner/repo` slug for a parsed reference. */
|
|
9
48
|
export declare function repoSlug(ref: RepoRef): string;
|
|
10
49
|
/**
|
|
@@ -14,11 +53,20 @@ export declare function repoSlug(ref: RepoRef): string;
|
|
|
14
53
|
export declare function normalizeRepoInput(input: string): RepoRef | null;
|
|
15
54
|
/** Collect every file inside a skill directory, including SKILL.md itself. */
|
|
16
55
|
export declare function collectRepoSkillFiles(tree: readonly RepoTreeItem[], dir: string): RepoFile[];
|
|
17
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* Compute an origin collection name. Multiple roots split by root, one root
|
|
58
|
+
* keeps the repo slug. The repo-root skill's name *is* the repo slug, so it
|
|
59
|
+
* never gets a trailing-slash suffix (`owner/repo/`).
|
|
60
|
+
*/
|
|
18
61
|
export declare function originForRoot(repo: string, rootsPresent: ReadonlySet<string>, root: string): string;
|
|
19
62
|
/** Discover importable skills from a repo tree. Invalid names are ignored. Roots are auto-derived from the top-level directory of each SKILL.md. */
|
|
20
63
|
export declare function discoverRepoEntries(tree: readonly RepoTreeItem[], repo: string, existingNames?: ReadonlySet<string>): RepoSkillEntry[];
|
|
21
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* Build a path→size manifest for one skill directory from a repo tree.
|
|
66
|
+
* Shares `skillTreeFiles` with collectRepoSkillFiles: a manifest that
|
|
67
|
+
* disagreed with the collected file set would make every update diff report
|
|
68
|
+
* "changed" forever.
|
|
69
|
+
*/
|
|
22
70
|
export declare function skillManifest(tree: readonly RepoTreeItem[], dir: string): Record<string, number>;
|
|
23
71
|
/**
|
|
24
72
|
* The real upstream directory of one tracked skill. Source records keep only
|
|
@@ -29,6 +77,12 @@ export declare function skillManifest(tree: readonly RepoTreeItem[], dir: string
|
|
|
29
77
|
* manifest's recorded blob path, then any same-name skill in the tree, then
|
|
30
78
|
* the flat root/name fallback. Passing the tree paths makes the lookup
|
|
31
79
|
* resilient to incomplete manifests and upstream moves.
|
|
80
|
+
*
|
|
81
|
+
* A repo-root record (root '') has no directory to resolve: its SKILL.md is a
|
|
82
|
+
* bare top-level path, so every nested lookup below would miss and the final
|
|
83
|
+
* fallback would hand back a bogus '/name'. Answer before them — including
|
|
84
|
+
* when the upstream SKILL.md is gone, so diffRemoteSkills reports a deletion
|
|
85
|
+
* instead of hunting for a skill that was never nested.
|
|
32
86
|
*/
|
|
33
87
|
export declare function skillDirOf(source: {
|
|
34
88
|
root: string;
|
|
@@ -38,6 +92,8 @@ export declare function skillDirOf(source: {
|
|
|
38
92
|
* Diff one source record against an upstream tree at treeSha: which tracked
|
|
39
93
|
* skills disappeared (no SKILL.md blob) and which changed (manifest baseline
|
|
40
94
|
* differs, or no baseline exists — treated as changed). Pure over the tree.
|
|
95
|
+
* The remote side is read through skillManifest, so the diff compares like
|
|
96
|
+
* with like against the baseline recorded at import time.
|
|
41
97
|
*/
|
|
42
98
|
export declare function diffRemoteSkills(tree: readonly RepoTreeItem[], source: {
|
|
43
99
|
root: string;
|
|
@@ -12,6 +12,22 @@ export declare class RepoFetchError extends Error {
|
|
|
12
12
|
export declare function setGithubToken(token: string | undefined): void;
|
|
13
13
|
/** Authorization header for api.github.com / raw.githubusercontent.com calls. */
|
|
14
14
|
export declare function githubAuthHeaders(): Record<string, string>;
|
|
15
|
+
/**
|
|
16
|
+
* 传输层:显式要求服务端返回未压缩实体。
|
|
17
|
+
*
|
|
18
|
+
* 为什么必须加:dsh 会把**启动环境**里的 `HTTP_PROXY`/`HTTPS_PROXY` 装成
|
|
19
|
+
* undici 的全局 dispatcher(`@deepseek-ai/dsh-http-proxy`),插件里的 `fetch`
|
|
20
|
+
* 因此走该代理。实测本机 Clash:经代理返回的响应会**丢掉 `content-encoding`
|
|
21
|
+
* 与 `content-type` 头,而 body 仍是 gzip**;undici 只依据 `content-encoding`
|
|
22
|
+
* 决定是否解压,于是 `response.json()` 拿到 gzip 二进制并抛错,被上层的
|
|
23
|
+
* catch 映射成 `invalid github response for <url>` —— 看起来像 GitHub 坏了。
|
|
24
|
+
* 实测对照(同一 URL、同一代理):默认 1331 字节解析失败,声明 identity 后
|
|
25
|
+
* 5245 字节解析成功。
|
|
26
|
+
*
|
|
27
|
+
* 代价是不走压缩(我们的响应体都很小),换来的是**无论中间代理是否改写头部
|
|
28
|
+
* 都能正确取到实体**,且用户无需为插件调整启动命令。
|
|
29
|
+
*/
|
|
30
|
+
export declare const NO_COMPRESSION: Record<string, string>;
|
|
15
31
|
/** JSON API 默认请求头(含鉴权)。 */
|
|
16
32
|
export declare function apiHeaders(): Record<string, string>;
|
|
17
33
|
/**
|
package/lib/types/repo.d.ts
CHANGED
|
@@ -4,13 +4,16 @@
|
|
|
4
4
|
* Kept dependency-free and mostly pure so the root/origin rules are easy to
|
|
5
5
|
* test. Roots are auto-derived: any top-level directory that contains a
|
|
6
6
|
* `**\/SKILL.md` (e.g. `skills/**`, `design-templates/**`, `templates/**`,
|
|
7
|
-
* `workflows/**`) is treated as a skill root. No hard-coded allowlist.
|
|
7
|
+
* `workflows/**`) is treated as a skill root. No hard-coded allowlist. A
|
|
8
|
+
* `SKILL.md` at the repo root is the empty-string root (`REPO_ROOT`): the
|
|
9
|
+
* whole repo is one skill, named after the repo, with top-level dot entries
|
|
10
|
+
* treated as repo tooling rather than skill content.
|
|
8
11
|
*
|
|
9
12
|
* The implementation now lives under `./repo/` (types, discovery, api,
|
|
10
13
|
* install) and this module is a barrel that re-exports it.
|
|
11
14
|
*/
|
|
12
|
-
export { RepoFetchError, fetchError, fetchJson, fetchJsonCached, githubAuthHeaders, isAbortError, setGithubToken } from './repo/github-client.ts';
|
|
15
|
+
export { NO_COMPRESSION, RepoFetchError, apiHeaders, fetchError, fetchJson, fetchJsonCached, githubAuthHeaders, isAbortError, setGithubToken } from './repo/github-client.ts';
|
|
13
16
|
export type { RepoRef, RepoTreeItem, RepoFile } from './repo/types.ts';
|
|
14
|
-
export { repoSlug, normalizeRepoInput, collectRepoSkillFiles, originForRoot, discoverRepoEntries, skillManifest, skillDirOf, diffRemoteSkills, repoSkillEntry, } from './repo/discovery.ts';
|
|
17
|
+
export { REPO_ROOT, isRepoRoot, repoSlug, normalizeRepoInput, collectRepoSkillFiles, originForRoot, discoverRepoEntries, skillManifest, skillDirOf, skillFileAt, skillPathIn, relativeToSkillDir, diffRemoteSkills, repoSkillEntry, } from './repo/discovery.ts';
|
|
15
18
|
export { loadRepoTree, getLatestReleaseTag, getRepoStats, listRepoReleases, listRepoBranches, getLatestCommit, loadRepoTreeAt, } from './repo/api.ts';
|
|
16
19
|
export { downloadGitHubFile, downloadRepoSkill, cleanupLeftoverImportDirs } from './repo/install.ts';
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* ./catalog-data.ts(目录装配);对外导出面保持不变。
|
|
8
8
|
*/
|
|
9
9
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
10
|
-
import type { WebRoute } from '@deepseek-ai/dsh-host-webserver';
|
|
10
|
+
import type { WebRoute, WebRouteKind } from '@deepseek-ai/dsh-host-webserver';
|
|
11
11
|
import { type SkillHubRouteDeps } from './deps.ts';
|
|
12
12
|
export { MAX_JSON_BODY_BYTES, STORE_ERROR_STATUS, isLoopbackRequest, pathExists, queryParam, readJsonBody, readString, readStrings, writeError, writeJson, writeRouteError, } from './http.ts';
|
|
13
13
|
export { configOf, disabledGate, homeOf, isWritableSource, resolveWritableSkill, savedOf, } from './deps.ts';
|
|
@@ -28,6 +28,13 @@ export type RouteHandler = (context: RouteContext) => Promise<void>;
|
|
|
28
28
|
/** One declarative route: path + accepted methods + the business handler. */
|
|
29
29
|
export interface RouteSpec {
|
|
30
30
|
path: string;
|
|
31
|
+
/**
|
|
32
|
+
* Match kind: 'exact' (default) matches the pathname verbatim; 'prefix'
|
|
33
|
+
* matches the path and everything beneath it. The webserver consults the
|
|
34
|
+
* exact table first and then resolves prefixes longest-first, so a prefix
|
|
35
|
+
* route is a catch-all that can never shadow a real route.
|
|
36
|
+
*/
|
|
37
|
+
kind?: WebRouteKind;
|
|
31
38
|
/** Accepted HTTP methods; anything else answers 405. */
|
|
32
39
|
methods: readonly ('GET' | 'POST')[];
|
|
33
40
|
/** POST requests must carry a JSON body (400 when missing/unparseable). */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 市场域路由:market 列表 / source 增删 / ref 锁定 / versions 选择器 /
|
|
3
|
-
* check 更新检查 / stats 星标统计 / sync
|
|
4
|
-
* handler 逻辑不变。
|
|
3
|
+
* source/check 更新检查 / stats 星标统计 / source/sync 对齐版本。
|
|
4
|
+
* 从 routes.ts 原样搬出,handler 逻辑不变。
|
|
5
5
|
*/
|
|
6
6
|
import { type RouteSpec, type SkillHubRouteDeps } from './helpers.ts';
|
|
7
7
|
/** 市场域全部路由 spec(由 routes.ts 经 createRoute 包上统一围栏)。 */
|
package/lib/types/routes.d.ts
CHANGED
|
@@ -17,6 +17,6 @@ export type { SkillHubRouteDeps, SkillLookupLike } from './routes/helpers.ts';
|
|
|
17
17
|
/**
|
|
18
18
|
* Build every /api/skill-hub route.
|
|
19
19
|
* @param deps - skill registry view + sidecar store.
|
|
20
|
-
* @returns the exact-path routes.
|
|
20
|
+
* @returns the exact-path routes plus the family's 404 catch-all.
|
|
21
21
|
*/
|
|
22
22
|
export declare function makeRoutes(deps: SkillHubRouteDeps): WebRoute[];
|