@sparkelf/dsh-client-ui-skill-center 0.2.0-rc.9

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/lib/index.js ADDED
@@ -0,0 +1,396 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join, resolve } from "node:path";
5
+ //#region lib/types/index.js
6
+ /**
7
+ * Skill center — host half. Serves the skill-center data source over the
8
+ * `/api/dsh-skill-center` route family: list grouped by source, enable or disable
9
+ * model invocation, create, delete into a recoverable trash, and health.
10
+ *
11
+ * Reading goes through the official `ctx.skills` registry, so the catalog, its
12
+ * provider precedence, and its hot reload are the harness's rather than a second
13
+ * scanner's. The three writes have no official equivalent — the registry is
14
+ * read-only — so they are implemented here against the skill file's YAML
15
+ * frontmatter.
16
+ *
17
+ * @module @sparkelf/dsh-client-ui-skill-center
18
+ */
19
+ /** Stable cordis plugin name. */
20
+ const name = "ui-skill-center";
21
+ /** Services required before the routes can mount. */
22
+ const inject = [
23
+ "webServer",
24
+ "skills",
25
+ "sessions"
26
+ ];
27
+ /** Route paths, mirrored by the browser half. */
28
+ const ROUTES = {
29
+ list: "/api/dsh-skill-center/list",
30
+ setEnabled: "/api/dsh-skill-center/set-enabled",
31
+ create: "/api/dsh-skill-center/create",
32
+ delete: "/api/dsh-skill-center/delete",
33
+ health: "/api/dsh-skill-center/health"
34
+ };
35
+ /**
36
+ * Groups the panel renders, in order. The level a skill resolves to selects its
37
+ * group; anything matching no convention lands in the custom group.
38
+ */
39
+ const SOURCE_GROUPS = [
40
+ {
41
+ key: "bundled",
42
+ title: "系统内置",
43
+ hint: "随 Harness 一同安装,不能删除"
44
+ },
45
+ {
46
+ key: "runtime",
47
+ title: "运行时注册",
48
+ hint: "由已安装的插件在运行时注册"
49
+ },
50
+ {
51
+ key: "user-agents",
52
+ title: "用户 ~/.agents/skills",
53
+ hint: "在用户 agents 目录下,对所有项目生效"
54
+ },
55
+ {
56
+ key: "user-dsh",
57
+ title: "用户 ~/.dsh/skills",
58
+ hint: "在用户 dsh 目录下,对所有项目生效"
59
+ },
60
+ {
61
+ key: "project-agents",
62
+ title: "项目 .agents/skills",
63
+ hint: "随项目提交,对协作者共享"
64
+ },
65
+ {
66
+ key: "project-dsh",
67
+ title: "项目 .dsh/skills",
68
+ hint: "项目本地技能,通常不提交"
69
+ },
70
+ {
71
+ key: "custom",
72
+ title: "自定义目录",
73
+ hint: "由本插件的 customSkillDirs 配置"
74
+ }
75
+ ];
76
+ /**
77
+ * The user skill root convention.
78
+ * @param dshHome - the user's dsh config root, usually `~/.dsh`.
79
+ * @returns the directory holding user-level skills.
80
+ */
81
+ function userSkillRoot(dshHome) {
82
+ return join(dshHome, "skills");
83
+ }
84
+ /**
85
+ * The project skill root convention.
86
+ * @param projectRoot - the project's root directory.
87
+ * @returns the directory holding that project's skills.
88
+ */
89
+ function projectSkillRoot(projectRoot) {
90
+ return join(projectRoot, ".dsh", "skills");
91
+ }
92
+ /**
93
+ * The nearest ancestor holding a `.git` entry.
94
+ * @param cwd - the directory to search upward from.
95
+ * @returns the project root, or `cwd` itself when no ancestor holds `.git`.
96
+ */
97
+ function findProjectRoot(cwd) {
98
+ let current = resolve(cwd);
99
+ for (;;) {
100
+ if (existsSync(join(current, ".git"))) return current;
101
+ const parent = dirname(current);
102
+ if (parent === current) return resolve(cwd);
103
+ current = parent;
104
+ }
105
+ }
106
+ /**
107
+ * The display group a skill belongs to.
108
+ *
109
+ * The registry summary reports a discovery source rather than a file path, so
110
+ * grouping follows the source name the filesystem provider assigns to each root
111
+ * it scans. An unrecognized source falls through to the custom group.
112
+ * @param source - the registry's discovery source label.
113
+ * @param provider - the provider that owns the skill body.
114
+ * @returns the group key.
115
+ */
116
+ function levelOfSource(source, provider) {
117
+ if (provider === "runtime") return "runtime";
118
+ const s = source.toLowerCase();
119
+ if (s.includes("bundled")) return "bundled";
120
+ if (s.includes(".agents")) return "project-agents";
121
+ if (s.includes(".dsh")) return "project-dsh";
122
+ if (s.includes("agents")) return "user-agents";
123
+ if (s.includes("dsh")) return "user-dsh";
124
+ if (s.includes("runtime")) return "runtime";
125
+ return "custom";
126
+ }
127
+ /**
128
+ * Read `disable-model-invocation` from a skill file.
129
+ * @param source - the skill file's content.
130
+ * @returns the flag when the frontmatter declares it, otherwise undefined.
131
+ */
132
+ function readDisabledFlag(source) {
133
+ const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(source);
134
+ if (match === null) return void 0;
135
+ const line = /^\s*disable-model-invocation\s*:\s*(true|false)\s*$/m.exec(match[1] ?? "");
136
+ return line === null ? void 0 : line[1] === "true";
137
+ }
138
+ /**
139
+ * Rewrite `disable-model-invocation`, adding it when absent.
140
+ *
141
+ * The field is one YAML scalar in the leading frontmatter block, so this edits
142
+ * that line rather than re-serializing the document: comments, key order, and
143
+ * the body all survive unchanged.
144
+ * @param source - the skill file's current content.
145
+ * @param disabled - the value to write.
146
+ * @returns the new content.
147
+ */
148
+ function withDisabledFlag(source, disabled) {
149
+ const match = /^(---\r?\n)([\s\S]*?)(\r?\n---)/.exec(source);
150
+ if (match === null) return "---\ndisable-model-invocation: " + String(disabled) + "\n---\n" + source;
151
+ const body = match[2] ?? "";
152
+ const field = /^\s*disable-model-invocation\s*:.*$/m;
153
+ const next = field.test(body) ? body.replace(field, "disable-model-invocation: " + String(disabled)) : body + "\ndisable-model-invocation: " + String(disabled);
154
+ return (match[1] ?? "---\n") + next + (match[3] ?? "\n---") + source.slice(match[0].length);
155
+ }
156
+ /**
157
+ * Build the SKILL.md a create writes.
158
+ * @param name - the skill's kebab-case name.
159
+ * @param description - the routing description.
160
+ * @param whenToUse - extra routing guidance, omitted when undefined.
161
+ * @param content - the instruction body; an empty value yields a heading.
162
+ * @returns the complete file content.
163
+ */
164
+ function buildSkillContent(name, description, whenToUse, content) {
165
+ const lines = [
166
+ "---",
167
+ "name: " + name,
168
+ "description: " + description
169
+ ];
170
+ if (whenToUse !== void 0 && whenToUse.trim() !== "") lines.push("when-to-use: " + whenToUse);
171
+ lines.push("---", "");
172
+ lines.push(content.trim() === "" ? "# " + name : content.trim());
173
+ return lines.join("\n") + "\n";
174
+ }
175
+ /** Write a JSON response. */
176
+ function writeJson(res, status, body) {
177
+ const text = JSON.stringify(body);
178
+ res.writeHead(status, {
179
+ "content-type": "application/json; charset=utf-8",
180
+ "content-length": String(Buffer.byteLength(text))
181
+ });
182
+ res.end(text);
183
+ }
184
+ /** Read a bounded JSON request body. */
185
+ async function readJson(req, maxBytes) {
186
+ const chunks = [];
187
+ let total = 0;
188
+ for await (const chunk of req) {
189
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
190
+ total += buf.length;
191
+ if (total > maxBytes) throw new Error("request body too large");
192
+ chunks.push(buf);
193
+ }
194
+ if (total === 0) return {};
195
+ const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
196
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("request body must be a JSON object");
197
+ return parsed;
198
+ }
199
+ /** The list handler: group the registry snapshot by source. */
200
+ async function handleList(deps, cwd) {
201
+ const projectRoots = [...new Set(deps.activeSessionCwds().map(findProjectRoot).concat(findProjectRoot(cwd)))];
202
+ const snapshot = await deps.snapshotProject(cwd);
203
+ const byGroup = new Map(SOURCE_GROUPS.map((g) => [g.key, []]));
204
+ for (const skill of snapshot.skills) {
205
+ const level = levelOfSource(skill.source, skill.provider);
206
+ const entry = {
207
+ name: skill.name,
208
+ description: skill.description,
209
+ level,
210
+ provider: skill.provider,
211
+ modelInvocable: skill.invocation.modelInvocable,
212
+ userInvocable: skill.invocation.userInvocable,
213
+ ...skill.whenToUse === void 0 ? {} : { whenToUse: skill.whenToUse }
214
+ };
215
+ (byGroup.get(level) ?? byGroup.get("custom"))?.push(entry);
216
+ }
217
+ const groups = [];
218
+ for (const g of SOURCE_GROUPS) {
219
+ const skills = (byGroup.get(g.key) ?? []).sort((a, b) => a.name.localeCompare(b.name));
220
+ if (skills.length > 0) groups.push({
221
+ key: g.key,
222
+ title: g.title,
223
+ hint: g.hint,
224
+ skills
225
+ });
226
+ }
227
+ return {
228
+ cwd,
229
+ projectRoots,
230
+ complete: snapshot.complete,
231
+ groups
232
+ };
233
+ }
234
+ /** The set-enabled handler: rewrite the skill's frontmatter flag. */
235
+ async function handleSetEnabled(deps, body, cwd) {
236
+ const path = await resolveSkillPath(deps, requireString(body.name, "name"), body, cwd);
237
+ const enabled = body.enabled === true;
238
+ const source = await readFile(path, "utf8");
239
+ const before = readDisabledFlag(source);
240
+ const next = withDisabledFlag(source, !enabled);
241
+ if (next !== source) await writeFile(path, next, "utf8");
242
+ return {
243
+ name: requireString(body.name, "name"),
244
+ enabled,
245
+ modelInvocable: enabled,
246
+ changed: before !== !enabled
247
+ };
248
+ }
249
+ /** The create handler: write a new SKILL.md under the chosen root. */
250
+ async function handleCreate(deps, body) {
251
+ const root = body.root === "project" ? "project" : "user";
252
+ const skillName = requireString(body.name, "name");
253
+ if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(skillName)) throw new Error("skill name must be kebab-case");
254
+ const dir = join(root === "project" ? projectSkillRoot(deps.activeSessionCwds()[0] ?? process.cwd()) : userSkillRoot(deps.dshHome), skillName);
255
+ const target = join(dir, "SKILL.md");
256
+ await mkdir(dir, { recursive: true });
257
+ await writeFile(target, buildSkillContent(skillName, requireString(body.description, "description"), typeof body.whenToUse === "string" ? body.whenToUse : void 0, typeof body.content === "string" ? body.content : ""), "utf8");
258
+ return {
259
+ ok: true,
260
+ name: skillName,
261
+ path: target
262
+ };
263
+ }
264
+ /** The delete handler: move the skill directory into a recoverable trash. */
265
+ async function handleDelete(deps, body, cwd) {
266
+ const path = await resolveSkillPath(deps, requireString(body.name, "name"), body, cwd);
267
+ if (!existsSync(path)) throw new Error("skill file not found");
268
+ const dir = dirname(path);
269
+ const trash = join(dirname(dir), ".trash", String(Date.now()) + "-" + (body.name === void 0 ? "skill" : String(body.name)));
270
+ await mkdir(dirname(trash), { recursive: true });
271
+ await rename(dir, trash);
272
+ return {
273
+ ok: true,
274
+ moved: trash
275
+ };
276
+ }
277
+ /** The workspace a request is about: its own `cwd` query, else the first active session's. */
278
+ function currentCwd(deps, req) {
279
+ try {
280
+ const asked = new URL(req.url ?? "/", "http://localhost").searchParams.get("cwd");
281
+ if (asked !== null && asked !== "") return asked;
282
+ } catch {}
283
+ return deps.activeSessionCwds()[0] ?? process.cwd();
284
+ }
285
+ /** One required string field. */
286
+ function requireString(value, field) {
287
+ if (typeof value !== "string" || value === "") throw new Error("missing " + field);
288
+ return value;
289
+ }
290
+ /**
291
+ * The absolute file path a write should touch.
292
+ *
293
+ * A caller-supplied path is honoured so the panel can act on the exact file it
294
+ * listed; otherwise the registry resolves it, which is also what rejects a
295
+ * skill the registry does not know.
296
+ * @param deps - the route dependencies.
297
+ * @param name - the skill name.
298
+ * @param body - the request body, which may carry an explicit path.
299
+ * @param cwd - the workspace the caller is viewing.
300
+ * @returns the resolved path.
301
+ * @throws when the skill has no file (a virtual or runtime-registered skill).
302
+ */
303
+ async function resolveSkillPath(deps, name, body, cwd) {
304
+ if (typeof body.path === "string" && body.path !== "") return body.path;
305
+ const path = await deps.pathOf(name, cwd);
306
+ if (path === void 0) throw new Error("skill \"" + name + "\" has no file on disk");
307
+ return path;
308
+ }
309
+ /**
310
+ * Build the skill-center routes.
311
+ * @param deps - resolved roots, the registry snapshot, and the session list.
312
+ * @returns the routes for `ctx.webServer.register`.
313
+ */
314
+ function makeRoutes(deps) {
315
+ return [
316
+ {
317
+ kind: "exact",
318
+ path: ROUTES.health,
319
+ handler: (_req, res) => writeJson(res, 200, { ok: true })
320
+ },
321
+ {
322
+ kind: "exact",
323
+ path: ROUTES.list,
324
+ handler: async (req, res) => {
325
+ try {
326
+ writeJson(res, 200, await handleList(deps, currentCwd(deps, req)));
327
+ } catch (error) {
328
+ deps.logger.warn(error);
329
+ writeJson(res, 500, { error: error instanceof Error ? error.message : String(error) });
330
+ }
331
+ }
332
+ },
333
+ {
334
+ kind: "exact",
335
+ path: ROUTES.setEnabled,
336
+ handler: async (req, res) => {
337
+ const cwd = currentCwd(deps, req);
338
+ try {
339
+ writeJson(res, 200, await handleSetEnabled(deps, await readJson(req, 1 << 20), cwd));
340
+ } catch (error) {
341
+ deps.logger.warn(error);
342
+ writeJson(res, 400, { error: error instanceof Error ? error.message : String(error) });
343
+ }
344
+ }
345
+ },
346
+ {
347
+ kind: "exact",
348
+ path: ROUTES.create,
349
+ handler: async (req, res) => {
350
+ try {
351
+ writeJson(res, 200, await handleCreate(deps, await readJson(req, 1 << 20)));
352
+ } catch (error) {
353
+ deps.logger.warn(error);
354
+ writeJson(res, 400, { error: error instanceof Error ? error.message : String(error) });
355
+ }
356
+ }
357
+ },
358
+ {
359
+ kind: "exact",
360
+ path: ROUTES.delete,
361
+ handler: async (req, res) => {
362
+ const cwd = currentCwd(deps, req);
363
+ try {
364
+ writeJson(res, 200, await handleDelete(deps, await readJson(req, 1 << 20), cwd));
365
+ } catch (error) {
366
+ deps.logger.warn(error);
367
+ writeJson(res, 400, { error: error instanceof Error ? error.message : String(error) });
368
+ }
369
+ }
370
+ }
371
+ ];
372
+ }
373
+ /**
374
+ * Mount the skill-center routes.
375
+ * @param ctx - host context carrying `webServer`, `skills`, and `sessions`.
376
+ * @param config - resolved plugin config.
377
+ */
378
+ function apply(ctx, config = {}) {
379
+ const deps = {
380
+ dshHome: config.dshHome ?? process.env.DSH_HOME ?? join(homedir(), ".dsh"),
381
+ agentsHome: config.agentsHome ?? process.env.DSH_AGENTS_HOME ?? join(homedir(), ".agents"),
382
+ customSkillDirs: config.customSkillDirs ?? [],
383
+ snapshotProject: (cwd) => ctx.skills.snapshot({ cwd }).then((s) => ({
384
+ skills: s.skills,
385
+ complete: s.complete
386
+ })),
387
+ pathOf: async (name, cwd) => (await ctx.skills.get(name, { cwd }))?.path,
388
+ activeSessionCwds: () => ctx.sessions.list().map((s) => s.header.cwd).filter((c) => typeof c === "string" && c !== ""),
389
+ logger: { warn: (error) => {
390
+ ctx.logger.warn(error);
391
+ } }
392
+ };
393
+ for (const route of makeRoutes(deps)) ctx.effect(() => ctx.webServer.register(route), "ui-skill-center: route " + route.path);
394
+ }
395
+ //#endregion
396
+ export { ROUTES, apply, buildSkillContent, findProjectRoot, inject, levelOfSource, makeRoutes, name, projectSkillRoot, readDisabledFlag, userSkillRoot, withDisabledFlag };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Skill center page. Browses the loaded skills grouped by source, toggles model
3
+ * invocation, creates a skill, and deletes one into a recoverable trash.
4
+ *
5
+ * The header, rows, and glyph frame follow the official plugin panel's layout so
6
+ * the page reads as one of the shell's own.
7
+ *
8
+ * @module @sparkelf/dsh-client-ui-skill-center/client/SkillCenterPage
9
+ */
10
+ import { type ReactNode } from 'react';
11
+ import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
12
+ import type { SkillCenterLocaleKey } from './locales.ts';
13
+ /** What the panel needs from its slot: the dictionary resolver. */
14
+ export interface SkillCenterPageProps {
15
+ t(key: SkillCenterLocaleKey): string;
16
+ }
17
+ /**
18
+ * The skill center panel.
19
+ * @param props - the slot's runtime share, providing the dictionary resolver.
20
+ * @returns the rendered panel.
21
+ */
22
+ export declare function SkillCenterPage({ t }: PropsRuntime<'main'> & SkillCenterPageProps): ReactNode;
23
+ //# sourceMappingURL=SkillCenterPage.d.ts.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The sidebar's Skill Center glyph.
3
+ *
4
+ * The icon comes from the shared primitives library, so the row matches every
5
+ * other sidebar entry instead of carrying a hand-drawn SVG.
6
+ *
7
+ * @module @sparkelf/dsh-client-ui-skill-center/client/SkillCenterPanelIcon
8
+ */
9
+ import type { ReactNode } from 'react';
10
+ import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
11
+ /**
12
+ * Render the glyph at the size the sidebar asks for.
13
+ * @param props - the sidebar's icon share: requested edge and selected state.
14
+ * @returns the icon element.
15
+ */
16
+ export declare function SkillCenterPanelIcon({ size }: PropsRuntime<'sidebar.panellist'>): ReactNode;
17
+ //# sourceMappingURL=SkillCenterPanelIcon.d.ts.map
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Skill center API client. Talks to the host route family over same-origin
3
+ * fetch; the host owns the trust fence.
4
+ *
5
+ * @module @sparkelf/dsh-client-ui-skill-center/client/api
6
+ */
7
+ /** One skill entry as the host serves it. */
8
+ export interface SkillEntry {
9
+ name: string;
10
+ description: string;
11
+ whenToUse?: string;
12
+ provider?: string;
13
+ level: string;
14
+ path?: string;
15
+ /** True when discovered through a symlink; deletion is refused. */
16
+ linked?: boolean;
17
+ modelInvocable: boolean;
18
+ userInvocable: boolean;
19
+ workspaceRoot?: string;
20
+ workspaceName?: string;
21
+ isActiveWorkspace?: boolean;
22
+ }
23
+ /** One group as the host serves it. */
24
+ export interface GroupPayload {
25
+ key: string;
26
+ title: string;
27
+ hint: string;
28
+ skills: SkillEntry[];
29
+ }
30
+ /** One selectable workspace. */
31
+ export interface WorkspaceItem {
32
+ root: string;
33
+ name: string;
34
+ active: boolean;
35
+ }
36
+ /** The list route's payload. */
37
+ export interface ListPayload {
38
+ cwd: string;
39
+ projectRoots: string[];
40
+ complete: boolean;
41
+ groups: GroupPayload[];
42
+ workspaces?: WorkspaceItem[];
43
+ }
44
+ /** One thrown API error carrying the host's message. */
45
+ export declare class ApiError extends Error {
46
+ }
47
+ /** Skill center API client. */
48
+ export declare class SkillApi {
49
+ /** Fetch the grouped skill list. */
50
+ list(cwd?: string): Promise<ListPayload>;
51
+ /** Enable or disable a skill by rewriting its frontmatter flag. */
52
+ setEnabled(name: string, path: string, enabled: boolean): Promise<{
53
+ name: string;
54
+ enabled: boolean;
55
+ }>;
56
+ /** Create a skill file under the user or project root. */
57
+ create(payload: {
58
+ root: 'user' | 'project';
59
+ name: string;
60
+ description: string;
61
+ whenToUse?: string;
62
+ content?: string;
63
+ }): Promise<{
64
+ ok: true;
65
+ name: string;
66
+ path: string;
67
+ }>;
68
+ /** Delete a skill by moving it into the recoverable trash. */
69
+ remove(name: string, path: string): Promise<{
70
+ ok: true;
71
+ moved: string;
72
+ }>;
73
+ private request;
74
+ }
75
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Skill center — browser half. Contributes the sidebar entry and the panel it
3
+ * opens through the same official slots the Plugins panel uses: a
4
+ * `sidebar.panellist` row and a keyed `main` page, with the sidebar shell
5
+ * owning the button, label, and selected state around the glyph.
6
+ *
7
+ * @module @sparkelf/dsh-client-ui-skill-center/client
8
+ */
9
+ import type { Context } from '@deepseek-ai/cordis';
10
+ import type { MainPanelId } from '@deepseek-ai/dsh-client-ui-layout/client';
11
+ import { type SkillCenterLocaleKey } from './locales.ts';
12
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
13
+ interface LocaleNamespaceMap {
14
+ /** Skill center panel copy. */
15
+ 'skillCenter': SkillCenterLocaleKey;
16
+ }
17
+ }
18
+ /** Dictionary namespace owned by this plugin. */
19
+ export declare const NS = "skillCenter";
20
+ /** The id shared by the sidebar entry and the main panel it opens. */
21
+ export declare const PANEL_ID: MainPanelId;
22
+ /** Services required by the sidebar and page registrations. */
23
+ export declare const inject: string[];
24
+ /**
25
+ * Contribute the Skill Center entry and the page it opens.
26
+ * @param ctx - the browser plugin context.
27
+ */
28
+ export declare function apply(ctx: Context): void;
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Skill center dictionaries. Keys are typed so the panel and the sidebar entry
3
+ * cannot reference a string that does not exist in both languages.
4
+ *
5
+ * @module @sparkelf/dsh-client-ui-skill-center/client/locales
6
+ */
7
+ /** Every string this plugin renders. */
8
+ export interface SkillCenterLocale {
9
+ 'panel': string;
10
+ 'panel.intro': string;
11
+ 'search.placeholder': string;
12
+ 'search.clear': string;
13
+ 'search.noMatches': string;
14
+ 'action.refresh': string;
15
+ 'action.create': string;
16
+ 'action.delete': string;
17
+ 'action.cancel': string;
18
+ 'action.confirm': string;
19
+ 'state.loading': string;
20
+ 'state.empty': string;
21
+ 'state.failed': string;
22
+ 'skill.enabled': string;
23
+ 'skill.disabled': string;
24
+ 'skill.modelInvocable': string;
25
+ 'skill.userInvocable': string;
26
+ 'skill.linked': string;
27
+ 'create.title': string;
28
+ 'create.name': string;
29
+ 'create.description': string;
30
+ 'create.whenToUse': string;
31
+ 'create.content': string;
32
+ 'create.root': string;
33
+ 'create.rootUser': string;
34
+ 'create.rootProject': string;
35
+ 'delete.confirm': string;
36
+ }
37
+ /** Locale keys, for callers that resolve them dynamically. */
38
+ export type SkillCenterLocaleKey = keyof SkillCenterLocale;
39
+ /** Simplified Chinese dictionary. */
40
+ export declare const zh: SkillCenterLocale;
41
+ /** English dictionary. */
42
+ export declare const en: SkillCenterLocale;
43
+ //# sourceMappingURL=locales.d.ts.map