dsh-skills-manager 0.1.0 → 0.1.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/README.md +14 -13
- package/lib/client.js +95 -199
- package/lib/client.js.map +1 -1
- package/lib/index.js +213 -453
- package/lib/types/client/index.js +42 -81
- package/lib/types/index.d.ts +7 -6
- package/lib/types/index.js +194 -399
- package/lib/types/state.js +13 -39
- package/lib/types/types.d.ts +9 -17
- package/package.json +2 -2
- package/plugin-design.md +4 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A tree-external DeepSeek Harness plugin for managing reusable Agent Skills.
|
|
4
4
|
|
|
5
|
-
It adds a **Skill 管理** item to the Web Settings sidebar and a wrench control beside the composer attachment button. Skills are
|
|
5
|
+
It adds a **Skill 管理** item to the Web Settings sidebar and a wrench control beside the composer attachment button. Skills are installed into a private Harness library, then enabled independently at two scopes:
|
|
6
6
|
|
|
7
7
|
- **Global**: available from every workspace.
|
|
8
8
|
- **Project**: available from one registered workspace and its child directories.
|
|
@@ -11,7 +11,7 @@ An imported skill is disabled by default. The plugin uses the official `ctx.skil
|
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
14
|
-
Build a tarball or install the package from a trusted source:
|
|
14
|
+
The bundled `skills` CLI requires Node.js 22.20.0 or newer. Build a tarball or install the package from a trusted source:
|
|
15
15
|
|
|
16
16
|
```sh
|
|
17
17
|
pnpm install
|
|
@@ -32,25 +32,26 @@ After changing the bundle or installed dependencies, restart the Harness service
|
|
|
32
32
|
1. Open Web Settings, then select **Skill 管理** in the sidebar.
|
|
33
33
|
2. Choose **本机** to select a local skill folder, or **仓库** to enter a GitHub `owner/repo`, HTTPS URL, SSH URL, or other Git URL.
|
|
34
34
|
3. Enable **全局** or select a workspace and enable **项目**.
|
|
35
|
-
4.
|
|
35
|
+
4. Use the refresh icon on a row to reinstall that skill from the source recorded in `skills-lock.json`.
|
|
36
36
|
5. Use the wrench control beside the composer attachment button to toggle installed skills for the current session workspace only.
|
|
37
|
-
6. Remove an installed skill with **移除**. This deletes only the plugin's
|
|
37
|
+
6. Remove an installed skill with **移除**. This deletes only the plugin's private library entry, not the local source or remote repository.
|
|
38
38
|
|
|
39
|
-
Repository imports
|
|
39
|
+
Repository imports and refreshes are delegated to the `skills` CLI. It supports GitHub `owner/repo`, HTTPS, SSH, Git and file URLs, and reuses the machine's existing Git credential helper or SSH configuration. Do not put credentials in repository URLs; the plugin rejects embedded credentials before invoking the CLI.
|
|
40
40
|
|
|
41
|
-
Updates preserve global and project enablement.
|
|
42
|
-
|
|
43
|
-
The imported format follows the official filesystem provider: directory bundles use `<name>/SKILL.md`; flat skills use `<name>.md`. YAML frontmatter must contain a kebab-case `name` and non-empty `description`. Companion resources are copied with directory bundles.
|
|
41
|
+
Updates preserve global and project enablement. New imports and refreshes require standard directory bundles: `<name>/SKILL.md` with kebab-case `name` and non-empty `description`. Flat `<name>.md` imports are not supported.
|
|
44
42
|
|
|
45
43
|
## Storage
|
|
46
44
|
|
|
47
45
|
By default the plugin uses the actual Harness home resolved from `DSH_HOME`:
|
|
48
46
|
|
|
49
47
|
```text
|
|
50
|
-
$DSH_HOME/skill-manager/
|
|
48
|
+
$DSH_HOME/skill-manager/.agents/skills/
|
|
49
|
+
$DSH_HOME/skill-manager/skills-lock.json
|
|
51
50
|
$DSH_HOME/skill-manager/state.json
|
|
52
51
|
```
|
|
53
52
|
|
|
53
|
+
`skills-lock.json` is the only source-of-origin record. `state.json` stores only global and project enablement.
|
|
54
|
+
|
|
54
55
|
Override the storage root in `cordis.patch.yml`:
|
|
55
56
|
|
|
56
57
|
```yaml
|
|
@@ -69,13 +70,13 @@ pnpm test
|
|
|
69
70
|
pnpm validate
|
|
70
71
|
```
|
|
71
72
|
|
|
72
|
-
The test suite covers scope resolution,
|
|
73
|
+
The test suite covers scope resolution, current-state validation, local and Git refreshes through the private CLI library, flat-format rejection, credential URL rejection, and removal cleanup. `pnpm validate` checks the DSH bundle shape.
|
|
73
74
|
|
|
74
75
|
## Design boundaries
|
|
75
76
|
|
|
76
|
-
- This plugin
|
|
77
|
-
- It does not rewrite an imported `SKILL.md`
|
|
78
|
-
- Skills already present in other filesystem roots remain owned by those roots; import them into this library when they should be managed here.
|
|
77
|
+
- This plugin runs the packaged `skills` CLI with an argv array and a private storage `cwd`; it never modifies the active workspace's `.agents/skills` directory.
|
|
78
|
+
- It does not rewrite an imported `SKILL.md` or alter the source folder or repository. Broken or escaping source symlinks are rejected before the CLI copies anything.
|
|
79
|
+
- Skills already present in other filesystem roots remain owned by those roots; import them into this private library when they should be managed here.
|
|
79
80
|
- Import and mutation RPCs are loopback-only because they write local files.
|
|
80
81
|
- DSH is in developer preview; bundle and Cordis APIs can change between Harness releases.
|
|
81
82
|
|
package/lib/client.js
CHANGED
|
@@ -135,26 +135,26 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
135
135
|
|
|
136
136
|
//#endregion
|
|
137
137
|
//#region node_modules/.pnpm/lucide-react@1.31.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/wrench.mjs
|
|
138
|
-
const __iconNode$
|
|
138
|
+
const __iconNode$11 = [["path", {
|
|
139
139
|
d: "M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z",
|
|
140
140
|
key: "1ngwbx"
|
|
141
141
|
}]];
|
|
142
|
-
const Wrench = createLucideIcon("wrench", __iconNode$
|
|
142
|
+
const Wrench = createLucideIcon("wrench", __iconNode$11);
|
|
143
143
|
|
|
144
144
|
//#endregion
|
|
145
145
|
//#region node_modules/.pnpm/lucide-react@1.31.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/plus.mjs
|
|
146
|
-
const __iconNode$
|
|
146
|
+
const __iconNode$10 = [["path", {
|
|
147
147
|
d: "M5 12h14",
|
|
148
148
|
key: "1ays0h"
|
|
149
149
|
}], ["path", {
|
|
150
150
|
d: "M12 5v14",
|
|
151
151
|
key: "s699le"
|
|
152
152
|
}]];
|
|
153
|
-
const Plus = createLucideIcon("plus", __iconNode$
|
|
153
|
+
const Plus = createLucideIcon("plus", __iconNode$10);
|
|
154
154
|
|
|
155
155
|
//#endregion
|
|
156
156
|
//#region node_modules/.pnpm/lucide-react@1.31.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/refresh-cw.mjs
|
|
157
|
-
const __iconNode$
|
|
157
|
+
const __iconNode$9 = [
|
|
158
158
|
["path", {
|
|
159
159
|
d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",
|
|
160
160
|
key: "v9h5vc"
|
|
@@ -172,11 +172,11 @@ const __iconNode$10 = [
|
|
|
172
172
|
key: "1cv678"
|
|
173
173
|
}]
|
|
174
174
|
];
|
|
175
|
-
const RefreshCw = createLucideIcon("refresh-cw", __iconNode$
|
|
175
|
+
const RefreshCw = createLucideIcon("refresh-cw", __iconNode$9);
|
|
176
176
|
|
|
177
177
|
//#endregion
|
|
178
178
|
//#region node_modules/.pnpm/lucide-react@1.31.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/search.mjs
|
|
179
|
-
const __iconNode$
|
|
179
|
+
const __iconNode$8 = [["path", {
|
|
180
180
|
d: "m21 21-4.34-4.34",
|
|
181
181
|
key: "14j7rj"
|
|
182
182
|
}], ["circle", {
|
|
@@ -185,22 +185,22 @@ const __iconNode$9 = [["path", {
|
|
|
185
185
|
r: "8",
|
|
186
186
|
key: "4ej97u"
|
|
187
187
|
}]];
|
|
188
|
-
const Search = createLucideIcon("search", __iconNode$
|
|
188
|
+
const Search = createLucideIcon("search", __iconNode$8);
|
|
189
189
|
|
|
190
190
|
//#endregion
|
|
191
191
|
//#region node_modules/.pnpm/lucide-react@1.31.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/x.mjs
|
|
192
|
-
const __iconNode$
|
|
192
|
+
const __iconNode$7 = [["path", {
|
|
193
193
|
d: "M18 6 6 18",
|
|
194
194
|
key: "1bl5f8"
|
|
195
195
|
}], ["path", {
|
|
196
196
|
d: "m6 6 12 12",
|
|
197
197
|
key: "d8bk6v"
|
|
198
198
|
}]];
|
|
199
|
-
const X = createLucideIcon("x", __iconNode$
|
|
199
|
+
const X = createLucideIcon("x", __iconNode$7);
|
|
200
200
|
|
|
201
201
|
//#endregion
|
|
202
202
|
//#region node_modules/.pnpm/lucide-react@1.31.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/trash-2.mjs
|
|
203
|
-
const __iconNode$
|
|
203
|
+
const __iconNode$6 = [
|
|
204
204
|
["path", {
|
|
205
205
|
d: "M10 11v6",
|
|
206
206
|
key: "nco0om"
|
|
@@ -222,11 +222,11 @@ const __iconNode$7 = [
|
|
|
222
222
|
key: "e791ji"
|
|
223
223
|
}]
|
|
224
224
|
];
|
|
225
|
-
const Trash2 = createLucideIcon("trash-2", __iconNode$
|
|
225
|
+
const Trash2 = createLucideIcon("trash-2", __iconNode$6);
|
|
226
226
|
|
|
227
227
|
//#endregion
|
|
228
228
|
//#region node_modules/.pnpm/lucide-react@1.31.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/globe.mjs
|
|
229
|
-
const __iconNode$
|
|
229
|
+
const __iconNode$5 = [
|
|
230
230
|
["circle", {
|
|
231
231
|
cx: "12",
|
|
232
232
|
cy: "12",
|
|
@@ -242,11 +242,11 @@ const __iconNode$6 = [
|
|
|
242
242
|
key: "9i4pu4"
|
|
243
243
|
}]
|
|
244
244
|
];
|
|
245
|
-
const Globe = createLucideIcon("globe", __iconNode$
|
|
245
|
+
const Globe = createLucideIcon("globe", __iconNode$5);
|
|
246
246
|
|
|
247
247
|
//#endregion
|
|
248
248
|
//#region node_modules/.pnpm/lucide-react@1.31.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/folder-git.mjs
|
|
249
|
-
const __iconNode$
|
|
249
|
+
const __iconNode$4 = [
|
|
250
250
|
["circle", {
|
|
251
251
|
cx: "12",
|
|
252
252
|
cy: "13",
|
|
@@ -266,11 +266,11 @@ const __iconNode$5 = [
|
|
|
266
266
|
key: "1pygq7"
|
|
267
267
|
}]
|
|
268
268
|
];
|
|
269
|
-
const FolderGit = createLucideIcon("folder-git", __iconNode$
|
|
269
|
+
const FolderGit = createLucideIcon("folder-git", __iconNode$4);
|
|
270
270
|
|
|
271
271
|
//#endregion
|
|
272
272
|
//#region node_modules/.pnpm/lucide-react@1.31.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/git-branch.mjs
|
|
273
|
-
const __iconNode$
|
|
273
|
+
const __iconNode$3 = [
|
|
274
274
|
["path", {
|
|
275
275
|
d: "M15 6a9 9 0 0 0-9 9V3",
|
|
276
276
|
key: "1cii5b"
|
|
@@ -288,11 +288,11 @@ const __iconNode$4 = [
|
|
|
288
288
|
key: "fqmcym"
|
|
289
289
|
}]
|
|
290
290
|
];
|
|
291
|
-
const GitBranch = createLucideIcon("git-branch", __iconNode$
|
|
291
|
+
const GitBranch = createLucideIcon("git-branch", __iconNode$3);
|
|
292
292
|
|
|
293
293
|
//#endregion
|
|
294
294
|
//#region node_modules/.pnpm/lucide-react@1.31.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/package-open.mjs
|
|
295
|
-
const __iconNode$
|
|
295
|
+
const __iconNode$2 = [
|
|
296
296
|
["path", {
|
|
297
297
|
d: "M12 22v-9",
|
|
298
298
|
key: "x3hkom"
|
|
@@ -310,19 +310,19 @@ const __iconNode$3 = [
|
|
|
310
310
|
key: "12ttoo"
|
|
311
311
|
}]
|
|
312
312
|
];
|
|
313
|
-
const PackageOpen = createLucideIcon("package-open", __iconNode$
|
|
313
|
+
const PackageOpen = createLucideIcon("package-open", __iconNode$2);
|
|
314
314
|
|
|
315
315
|
//#endregion
|
|
316
316
|
//#region node_modules/.pnpm/lucide-react@1.31.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/puzzle.mjs
|
|
317
|
-
const __iconNode$
|
|
317
|
+
const __iconNode$1 = [["path", {
|
|
318
318
|
d: "M15.39 4.39a1 1 0 0 0 1.68-.474 2.5 2.5 0 1 1 3.014 3.015 1 1 0 0 0-.474 1.68l1.683 1.682a2.414 2.414 0 0 1 0 3.414L19.61 15.39a1 1 0 0 1-1.68-.474 2.5 2.5 0 1 0-3.014 3.015 1 1 0 0 1 .474 1.68l-1.683 1.682a2.414 2.414 0 0 1-3.414 0L8.61 19.61a1 1 0 0 0-1.68.474 2.5 2.5 0 1 1-3.014-3.015 1 1 0 0 0 .474-1.68l-1.683-1.682a2.414 2.414 0 0 1 0-3.414L4.39 8.61a1 1 0 0 1 1.68.474 2.5 2.5 0 1 0 3.014-3.015 1 1 0 0 1-.474-1.68l1.683-1.682a2.414 2.414 0 0 1 3.414 0z",
|
|
319
319
|
key: "w46dr5"
|
|
320
320
|
}]];
|
|
321
|
-
const Puzzle = createLucideIcon("puzzle", __iconNode$
|
|
321
|
+
const Puzzle = createLucideIcon("puzzle", __iconNode$1);
|
|
322
322
|
|
|
323
323
|
//#endregion
|
|
324
324
|
//#region node_modules/.pnpm/lucide-react@1.31.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/circle-alert.mjs
|
|
325
|
-
const __iconNode
|
|
325
|
+
const __iconNode = [
|
|
326
326
|
["circle", {
|
|
327
327
|
cx: "12",
|
|
328
328
|
cy: "12",
|
|
@@ -344,23 +344,14 @@ const __iconNode$1 = [
|
|
|
344
344
|
key: "4dfq90"
|
|
345
345
|
}]
|
|
346
346
|
];
|
|
347
|
-
const CircleAlert = createLucideIcon("circle-alert", __iconNode
|
|
348
|
-
|
|
349
|
-
//#endregion
|
|
350
|
-
//#region node_modules/.pnpm/lucide-react@1.31.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/check.mjs
|
|
351
|
-
const __iconNode = [["path", {
|
|
352
|
-
d: "M20 6 9 17l-5-5",
|
|
353
|
-
key: "1gmf2c"
|
|
354
|
-
}]];
|
|
355
|
-
const Check = createLucideIcon("check", __iconNode);
|
|
347
|
+
const CircleAlert = createLucideIcon("circle-alert", __iconNode);
|
|
356
348
|
|
|
357
349
|
//#endregion
|
|
358
350
|
//#region lib/types/client/index.js
|
|
359
351
|
const inject = [
|
|
360
352
|
"connection",
|
|
361
353
|
"slots",
|
|
362
|
-
"workspaces"
|
|
363
|
-
"sessions"
|
|
354
|
+
"workspaces"
|
|
364
355
|
];
|
|
365
356
|
const ICON_SPIN = ["dsh-skill-manager-spin", "to { transform: rotate(360deg); }"];
|
|
366
357
|
function ensureKeyframes() {
|
|
@@ -370,6 +361,10 @@ function ensureKeyframes() {
|
|
|
370
361
|
style.id = ICON_SPIN[0];
|
|
371
362
|
style.textContent = `
|
|
372
363
|
@keyframes ${ICON_SPIN[0]} { ${ICON_SPIN[1]} }
|
|
364
|
+
.dsh-skill-manager-row:hover {
|
|
365
|
+
border-color: ${tokens.borderStrong} !important;
|
|
366
|
+
background: ${tokens.surfaceHover} !important;
|
|
367
|
+
}
|
|
373
368
|
@container (max-width: 360px) {
|
|
374
369
|
.dsh-skill-manager-row { grid-template-columns: minmax(0, 1fr) !important; }
|
|
375
370
|
.dsh-skill-manager-row-icon { display: none !important; }
|
|
@@ -390,8 +385,7 @@ const tokens = {
|
|
|
390
385
|
success: "var(--fg-success, #16a34a)",
|
|
391
386
|
danger: "var(--fg-danger, #dc2626)",
|
|
392
387
|
secondary: "var(--fg-secondary, #64748b)",
|
|
393
|
-
surfaceHover: "var(--bg-hover, rgba(148, 163, 184, .08))"
|
|
394
|
-
surfaceActive: "var(--bg-active, rgba(99, 102, 241, .12))"
|
|
388
|
+
surfaceHover: "var(--bg-hover, rgba(148, 163, 184, .08))"
|
|
395
389
|
};
|
|
396
390
|
const panelStyle = {
|
|
397
391
|
containerType: "inline-size",
|
|
@@ -424,12 +418,6 @@ function buttonStyle(tone, disabled) {
|
|
|
424
418
|
color: "var(--fg-on-accent, #fff)",
|
|
425
419
|
border: "none"
|
|
426
420
|
};
|
|
427
|
-
if (tone === "danger") return {
|
|
428
|
-
...base,
|
|
429
|
-
background: "transparent",
|
|
430
|
-
color: tokens.danger,
|
|
431
|
-
border: `1px solid ${tokens.border}`
|
|
432
|
-
};
|
|
433
421
|
return {
|
|
434
422
|
...base,
|
|
435
423
|
background: "transparent",
|
|
@@ -471,18 +459,22 @@ const selectStyle = {
|
|
|
471
459
|
color: "inherit",
|
|
472
460
|
fontSize: 13
|
|
473
461
|
};
|
|
462
|
+
const checkboxStyle = {
|
|
463
|
+
width: 16,
|
|
464
|
+
height: 16,
|
|
465
|
+
margin: 0,
|
|
466
|
+
accentColor: tokens.accent,
|
|
467
|
+
flex: "0 0 auto"
|
|
468
|
+
};
|
|
474
469
|
function apply(ctx) {
|
|
475
|
-
const
|
|
476
|
-
const
|
|
477
|
-
const result = await
|
|
478
|
-
if (!result.ok) throw new Error(result.error.message);
|
|
479
|
-
return result.value;
|
|
480
|
-
};
|
|
481
|
-
const mutate = async (endpoint, payload) => {
|
|
482
|
-
const result = await client.connection.rpc.call("/skill-manager", endpoint, payload);
|
|
470
|
+
const connection = ctx.connection;
|
|
471
|
+
const request = async (endpoint, payload = {}) => {
|
|
472
|
+
const result = await connection.rpc.call("/skill-manager", endpoint, payload);
|
|
483
473
|
if (!result.ok) throw new Error(result.error.message);
|
|
484
474
|
return result.value;
|
|
485
475
|
};
|
|
476
|
+
const list = (projectRoot) => request("list", projectRoot ? { projectRoot } : {});
|
|
477
|
+
const mutate = request;
|
|
486
478
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
487
479
|
name: "settings.section",
|
|
488
480
|
id: "skills",
|
|
@@ -492,7 +484,7 @@ function apply(ctx) {
|
|
|
492
484
|
list,
|
|
493
485
|
mutate,
|
|
494
486
|
pickDirectory: () => ctx.workspaces.pickDirectory(),
|
|
495
|
-
loopback:
|
|
487
|
+
loopback: connection.isLoopback
|
|
496
488
|
})
|
|
497
489
|
}, SkillManagerSection));
|
|
498
490
|
ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
|
|
@@ -501,8 +493,7 @@ function apply(ctx) {
|
|
|
501
493
|
order: 10,
|
|
502
494
|
inject: () => ({
|
|
503
495
|
list,
|
|
504
|
-
mutate
|
|
505
|
-
sessions: ctx.sessions.list
|
|
496
|
+
mutate
|
|
506
497
|
})
|
|
507
498
|
}, ComposerSkillButton));
|
|
508
499
|
}
|
|
@@ -519,8 +510,9 @@ function SkillManagerSection(props) {
|
|
|
519
510
|
(0, react.useEffect)(() => {
|
|
520
511
|
ensureKeyframes();
|
|
521
512
|
}, []);
|
|
513
|
+
const userChoseGlobal = (0, react.useRef)(false);
|
|
522
514
|
(0, react.useEffect)(() => {
|
|
523
|
-
if (projectRoot === void 0 && workspaces.items.length > 0) setProjectRoot(workspaces.items[0].path);
|
|
515
|
+
if (projectRoot === void 0 && !userChoseGlobal.current && workspaces.items.length > 0) setProjectRoot(workspaces.items[0].path);
|
|
524
516
|
if (projectRoot !== void 0 && !workspaces.items.some((item) => item.path === projectRoot)) setProjectRoot(workspaces.items[0]?.path);
|
|
525
517
|
}, [projectRoot, workspaces.items]);
|
|
526
518
|
const refresh = (0, react.useCallback)(async () => {
|
|
@@ -550,18 +542,8 @@ function SkillManagerSection(props) {
|
|
|
550
542
|
setBusy(false);
|
|
551
543
|
}
|
|
552
544
|
}, []);
|
|
553
|
-
const
|
|
554
|
-
|
|
555
|
-
return (catalog?.entries ?? []).filter((entry) => normalized.length === 0 || `${entry.name} ${entry.description}`.toLocaleLowerCase().includes(normalized));
|
|
556
|
-
}, [catalog, query]);
|
|
557
|
-
const stats = (0, react.useMemo)(() => {
|
|
558
|
-
const entries = catalog?.entries ?? [];
|
|
559
|
-
return {
|
|
560
|
-
total: entries.length,
|
|
561
|
-
global: entries.filter((entry) => entry.globalEnabled).length,
|
|
562
|
-
project: entries.filter((entry) => entry.projectEnabled).length
|
|
563
|
-
};
|
|
564
|
-
}, [catalog]);
|
|
545
|
+
const normalizedQuery = query.trim().toLocaleLowerCase();
|
|
546
|
+
const filtered = (catalog?.entries ?? []).filter((entry) => normalizedQuery.length === 0 || `${entry.name} ${entry.description}`.toLocaleLowerCase().includes(normalizedQuery));
|
|
565
547
|
const importSkill = async () => {
|
|
566
548
|
const path = await pickDirectory();
|
|
567
549
|
if (path) await run(() => mutate("install", {
|
|
@@ -582,12 +564,8 @@ function SkillManagerSection(props) {
|
|
|
582
564
|
}
|
|
583
565
|
};
|
|
584
566
|
const updateSkill = async (entry) => {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
if (!entry.updateSupported || entry.updateError && !remote) {
|
|
588
|
-
path = await pickDirectory() ?? void 0;
|
|
589
|
-
if (!path) return;
|
|
590
|
-
}
|
|
567
|
+
const path = entry.updateSupported ? void 0 : await pickDirectory() ?? void 0;
|
|
568
|
+
if (!entry.updateSupported && !path) return;
|
|
591
569
|
await run(() => mutate("update", {
|
|
592
570
|
name: entry.name,
|
|
593
571
|
path,
|
|
@@ -625,15 +603,6 @@ function SkillManagerSection(props) {
|
|
|
625
603
|
gap: 8,
|
|
626
604
|
flexWrap: "wrap"
|
|
627
605
|
} }, (0, react.createElement)("button", {
|
|
628
|
-
type: "button",
|
|
629
|
-
style: buttonStyle("ghost", busy),
|
|
630
|
-
onClick: () => void run(() => mutate("check-updates", { projectRoot })),
|
|
631
|
-
disabled: busy,
|
|
632
|
-
title: "检查已记录来源是否有变化"
|
|
633
|
-
}, (0, react.createElement)(RefreshCw, {
|
|
634
|
-
size: 14,
|
|
635
|
-
"aria-hidden": true
|
|
636
|
-
}), "检查更新"), (0, react.createElement)("button", {
|
|
637
606
|
type: "button",
|
|
638
607
|
style: buttonStyle("ghost", busy || !loopback),
|
|
639
608
|
onClick: () => setRepositoryOpen((value) => !value),
|
|
@@ -727,35 +696,30 @@ function SkillManagerSection(props) {
|
|
|
727
696
|
minWidth: 0,
|
|
728
697
|
alignItems: "center",
|
|
729
698
|
gap: 8,
|
|
730
|
-
flexWrap: "wrap",
|
|
731
699
|
fontSize: 13,
|
|
732
700
|
color: tokens.secondary
|
|
733
|
-
} }, "项目范围", (0, react.createElement)("select", {
|
|
734
|
-
value: projectRoot ?? "",
|
|
735
|
-
onChange: (event) =>
|
|
701
|
+
} }, (0, react.createElement)("span", { style: { flex: "0 0 auto" } }, "项目范围"), (0, react.createElement)("select", {
|
|
702
|
+
value: projectRoot ?? "__global__",
|
|
703
|
+
onChange: (event) => {
|
|
704
|
+
const value = event.currentTarget.value;
|
|
705
|
+
if (value === "__global__") {
|
|
706
|
+
userChoseGlobal.current = true;
|
|
707
|
+
setProjectRoot(void 0);
|
|
708
|
+
} else {
|
|
709
|
+
userChoseGlobal.current = false;
|
|
710
|
+
setProjectRoot(value);
|
|
711
|
+
}
|
|
712
|
+
},
|
|
736
713
|
style: {
|
|
737
714
|
...selectStyle,
|
|
738
|
-
|
|
715
|
+
flex: "1 1 auto",
|
|
716
|
+
minWidth: 0,
|
|
717
|
+
maxWidth: "none"
|
|
739
718
|
}
|
|
740
|
-
}, (0, react.createElement)("option", { value: "" }, "只看全局"), ...workspaces.items.map((workspace) => (0, react.createElement)("option", {
|
|
719
|
+
}, (0, react.createElement)("option", { value: "__global__" }, "只看全局"), ...workspaces.items.map((workspace) => (0, react.createElement)("option", {
|
|
741
720
|
key: workspace.workspaceId,
|
|
742
721
|
value: workspace.path
|
|
743
|
-
}, workspace.title))))), (0, react.createElement)("div", {
|
|
744
|
-
display: "flex",
|
|
745
|
-
gap: 8,
|
|
746
|
-
flexWrap: "wrap"
|
|
747
|
-
} }, (0, react.createElement)(StatChip, {
|
|
748
|
-
label: "已安装",
|
|
749
|
-
value: stats.total
|
|
750
|
-
}), (0, react.createElement)(StatChip, {
|
|
751
|
-
label: "全局启用",
|
|
752
|
-
value: stats.global,
|
|
753
|
-
tone: "accent"
|
|
754
|
-
}), projectRoot !== void 0 ? (0, react.createElement)(StatChip, {
|
|
755
|
-
label: "项目启用",
|
|
756
|
-
value: stats.project,
|
|
757
|
-
tone: "accent"
|
|
758
|
-
}) : null), error ? (0, react.createElement)("div", {
|
|
722
|
+
}, workspace.title))))), error ? (0, react.createElement)("div", {
|
|
759
723
|
role: "alert",
|
|
760
724
|
style: {
|
|
761
725
|
display: "flex",
|
|
@@ -811,23 +775,6 @@ function SkillManagerSection(props) {
|
|
|
811
775
|
}))
|
|
812
776
|
}))));
|
|
813
777
|
}
|
|
814
|
-
function StatChip(props) {
|
|
815
|
-
const active = props.tone === "accent" && props.value > 0;
|
|
816
|
-
return (0, react.createElement)("span", { style: {
|
|
817
|
-
display: "inline-flex",
|
|
818
|
-
alignItems: "baseline",
|
|
819
|
-
gap: 6,
|
|
820
|
-
padding: "4px 10px",
|
|
821
|
-
borderRadius: 999,
|
|
822
|
-
fontSize: 12,
|
|
823
|
-
border: `1px solid ${active ? "rgba(99, 102, 241, .4)" : tokens.border}`,
|
|
824
|
-
color: active ? tokens.accent : tokens.secondary,
|
|
825
|
-
background: active ? "rgba(99, 102, 241, .08)" : "transparent"
|
|
826
|
-
} }, (0, react.createElement)("span", null, props.label), (0, react.createElement)("strong", { style: {
|
|
827
|
-
fontSize: 13,
|
|
828
|
-
fontWeight: 600
|
|
829
|
-
} }, String(props.value)));
|
|
830
|
-
}
|
|
831
778
|
function EmptyState(props) {
|
|
832
779
|
return (0, react.createElement)("div", { style: {
|
|
833
780
|
display: "grid",
|
|
@@ -848,8 +795,8 @@ function EmptyState(props) {
|
|
|
848
795
|
} }, props.hasAny ? "没有匹配的 skill,试试调整搜索词。" : "还没有导入任何 skill。点击右上角「导入」,从本机目录开始。"));
|
|
849
796
|
}
|
|
850
797
|
function ComposerSkillButton(props) {
|
|
851
|
-
const { session,
|
|
852
|
-
const projectRoot =
|
|
798
|
+
const { session, list, mutate } = props;
|
|
799
|
+
const projectRoot = session.cwd;
|
|
853
800
|
const [open, setOpen] = (0, react.useState)(false);
|
|
854
801
|
const containerRef = (0, react.useRef)(null);
|
|
855
802
|
(0, react.useEffect)(() => {
|
|
@@ -1051,11 +998,13 @@ function ProjectSkillPopover(props) {
|
|
|
1051
998
|
overflow: "hidden",
|
|
1052
999
|
textOverflow: "ellipsis",
|
|
1053
1000
|
whiteSpace: "nowrap"
|
|
1054
|
-
} }, entry.name), (0, react.createElement)(
|
|
1001
|
+
} }, entry.name), (0, react.createElement)("input", {
|
|
1002
|
+
type: "checkbox",
|
|
1055
1003
|
checked: entry.projectEnabled,
|
|
1056
1004
|
disabled: busy,
|
|
1057
|
-
onChange: (
|
|
1058
|
-
label: entry.name
|
|
1005
|
+
onChange: (event) => void toggle(entry, event.currentTarget.checked),
|
|
1006
|
+
"aria-label": `${entry.name} 项目开关`,
|
|
1007
|
+
style: checkboxStyle
|
|
1059
1008
|
})))), busy ? (0, react.createElement)("div", { style: {
|
|
1060
1009
|
display: "flex",
|
|
1061
1010
|
justifyContent: "flex-end",
|
|
@@ -1068,7 +1017,6 @@ function ProjectSkillPopover(props) {
|
|
|
1068
1017
|
}
|
|
1069
1018
|
function SkillRow(props) {
|
|
1070
1019
|
const { entry, projectLabel, projectAvailable, busy, onGlobalChange, onProjectChange, onUpdate, onRemove } = props;
|
|
1071
|
-
const [hover, setHover] = (0, react.useState)(false);
|
|
1072
1020
|
return (0, react.createElement)("article", {
|
|
1073
1021
|
className: "dsh-skill-manager-row",
|
|
1074
1022
|
style: {
|
|
@@ -1083,12 +1031,10 @@ function SkillRow(props) {
|
|
|
1083
1031
|
boxSizing: "border-box",
|
|
1084
1032
|
padding: "12px 14px",
|
|
1085
1033
|
borderRadius: 10,
|
|
1086
|
-
border: `1px solid ${
|
|
1087
|
-
background:
|
|
1034
|
+
border: `1px solid ${tokens.border}`,
|
|
1035
|
+
background: "transparent",
|
|
1088
1036
|
transition: "border-color .15s ease, background .15s ease"
|
|
1089
|
-
}
|
|
1090
|
-
onMouseEnter: () => setHover(true),
|
|
1091
|
-
onMouseLeave: () => setHover(false)
|
|
1037
|
+
}
|
|
1092
1038
|
}, (0, react.createElement)("div", {
|
|
1093
1039
|
className: "dsh-skill-manager-row-icon",
|
|
1094
1040
|
style: {
|
|
@@ -1128,18 +1074,7 @@ function SkillRow(props) {
|
|
|
1128
1074
|
active: entry.projectEnabled,
|
|
1129
1075
|
icon: FolderGit,
|
|
1130
1076
|
label: projectLabel
|
|
1131
|
-
}) : null, entry.
|
|
1132
|
-
flex: "0 0 auto",
|
|
1133
|
-
fontSize: 11,
|
|
1134
|
-
color: tokens.accent
|
|
1135
|
-
} }, "有更新") : null, entry.updateError ? (0, react.createElement)("span", {
|
|
1136
|
-
style: {
|
|
1137
|
-
flex: "0 0 auto",
|
|
1138
|
-
fontSize: 11,
|
|
1139
|
-
color: tokens.danger
|
|
1140
|
-
},
|
|
1141
|
-
title: entry.updateError
|
|
1142
|
-
}, "来源异常") : null), entry.description ? (0, react.createElement)("p", { style: {
|
|
1077
|
+
}) : null), entry.description ? (0, react.createElement)("p", { style: {
|
|
1143
1078
|
margin: 0,
|
|
1144
1079
|
color: tokens.secondary,
|
|
1145
1080
|
lineHeight: 1.45,
|
|
@@ -1160,27 +1095,31 @@ function SkillRow(props) {
|
|
|
1160
1095
|
gap: 10,
|
|
1161
1096
|
flexWrap: "wrap"
|
|
1162
1097
|
}
|
|
1163
|
-
}, (0, react.createElement)(
|
|
1098
|
+
}, (0, react.createElement)("input", {
|
|
1099
|
+
type: "checkbox",
|
|
1164
1100
|
checked: entry.globalEnabled,
|
|
1165
1101
|
disabled: busy,
|
|
1166
|
-
onChange: onGlobalChange,
|
|
1167
|
-
label: `${entry.name} 全局开关`,
|
|
1168
|
-
title: "全局启用"
|
|
1169
|
-
|
|
1102
|
+
onChange: (event) => onGlobalChange(event.currentTarget.checked),
|
|
1103
|
+
"aria-label": `${entry.name} 全局开关`,
|
|
1104
|
+
title: "全局启用",
|
|
1105
|
+
style: checkboxStyle
|
|
1106
|
+
}), projectAvailable ? (0, react.createElement)("input", {
|
|
1107
|
+
type: "checkbox",
|
|
1170
1108
|
checked: entry.projectEnabled,
|
|
1171
1109
|
disabled: busy,
|
|
1172
|
-
onChange: onProjectChange,
|
|
1173
|
-
label: `${entry.name} 项目开关`,
|
|
1174
|
-
title: `${projectLabel}
|
|
1110
|
+
onChange: (event) => onProjectChange(event.currentTarget.checked),
|
|
1111
|
+
"aria-label": `${entry.name} 项目开关`,
|
|
1112
|
+
title: `${projectLabel} 启用`,
|
|
1113
|
+
style: checkboxStyle
|
|
1175
1114
|
}) : null, (0, react.createElement)("button", {
|
|
1176
1115
|
type: "button",
|
|
1177
1116
|
style: {
|
|
1178
1117
|
...iconButtonStyle,
|
|
1179
|
-
color:
|
|
1118
|
+
color: tokens.secondary
|
|
1180
1119
|
},
|
|
1181
1120
|
disabled: busy,
|
|
1182
1121
|
onClick: onUpdate,
|
|
1183
|
-
title: entry.updateSupported
|
|
1122
|
+
title: entry.updateSupported ? entry.sourceType === "git" ? "从远程仓库更新" : "从原始目录更新" : "选择来源目录并更新",
|
|
1184
1123
|
"aria-label": `更新 ${entry.name}`
|
|
1185
1124
|
}, (0, react.createElement)(RefreshCw, {
|
|
1186
1125
|
size: 15,
|
|
@@ -1201,12 +1140,12 @@ function SkillRow(props) {
|
|
|
1201
1140
|
}))));
|
|
1202
1141
|
}
|
|
1203
1142
|
function SourceBadge(props) {
|
|
1204
|
-
const remote = props.entry.sourceType === "git"
|
|
1205
|
-
const local = props.entry.sourceType === "local"
|
|
1143
|
+
const remote = props.entry.sourceType === "git";
|
|
1144
|
+
const local = props.entry.sourceType === "local";
|
|
1206
1145
|
const Icon$1 = remote ? GitBranch : local ? FolderGit : CircleAlert;
|
|
1207
1146
|
const label = remote ? "Git" : local ? "本机" : "未知";
|
|
1208
1147
|
return (0, react.createElement)("span", {
|
|
1209
|
-
title: remote ? props.entry.sourceUrl : local ? props.entry.sourcePath : "
|
|
1148
|
+
title: remote ? props.entry.sourceUrl : local ? props.entry.sourcePath : "来源未知,更新时需选择本机来源目录",
|
|
1210
1149
|
style: {
|
|
1211
1150
|
display: "inline-flex",
|
|
1212
1151
|
alignItems: "center",
|
|
@@ -1252,49 +1191,6 @@ function StatusBadge(props) {
|
|
|
1252
1191
|
"aria-hidden": true
|
|
1253
1192
|
}), props.active ? `${props.label}开` : `${props.label}关`);
|
|
1254
1193
|
}
|
|
1255
|
-
function Toggle(props) {
|
|
1256
|
-
const { checked, disabled, onChange, label, title } = props;
|
|
1257
|
-
return (0, react.createElement)("button", {
|
|
1258
|
-
type: "button",
|
|
1259
|
-
role: "switch",
|
|
1260
|
-
"aria-checked": checked,
|
|
1261
|
-
"aria-label": label,
|
|
1262
|
-
title,
|
|
1263
|
-
disabled,
|
|
1264
|
-
onClick: () => onChange(!checked),
|
|
1265
|
-
style: {
|
|
1266
|
-
position: "relative",
|
|
1267
|
-
flex: "0 0 auto",
|
|
1268
|
-
width: 30,
|
|
1269
|
-
height: 18,
|
|
1270
|
-
padding: 0,
|
|
1271
|
-
border: "none",
|
|
1272
|
-
borderRadius: 999,
|
|
1273
|
-
background: checked ? tokens.accent : "rgba(148, 163, 184, .35)",
|
|
1274
|
-
cursor: disabled ? "default" : "pointer",
|
|
1275
|
-
opacity: disabled ? .55 : 1,
|
|
1276
|
-
transition: "background .15s ease"
|
|
1277
|
-
}
|
|
1278
|
-
}, (0, react.createElement)("span", {
|
|
1279
|
-
"aria-hidden": true,
|
|
1280
|
-
style: {
|
|
1281
|
-
position: "absolute",
|
|
1282
|
-
top: 2,
|
|
1283
|
-
left: checked ? 14 : 2,
|
|
1284
|
-
width: 14,
|
|
1285
|
-
height: 14,
|
|
1286
|
-
borderRadius: 999,
|
|
1287
|
-
background: "#fff",
|
|
1288
|
-
boxShadow: "0 1px 2px rgba(0, 0, 0, .3)",
|
|
1289
|
-
transition: "left .15s ease",
|
|
1290
|
-
display: "grid",
|
|
1291
|
-
placeItems: "center"
|
|
1292
|
-
}
|
|
1293
|
-
}, checked ? (0, react.createElement)(Check, {
|
|
1294
|
-
size: 10,
|
|
1295
|
-
"aria-hidden": true
|
|
1296
|
-
}) : null));
|
|
1297
|
-
}
|
|
1298
1194
|
function currentThemeColors() {
|
|
1299
1195
|
if (typeof document === "undefined") return {
|
|
1300
1196
|
background: "#151517",
|