dsh-skill-importer 0.2.3 → 0.2.4

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/client.js CHANGED
@@ -225,14 +225,24 @@ window.__ModuleLoader__.load({
225
225
  });
226
226
  }
227
227
  /** The settings section's page body. */
228
- function SkillImporterSection({ t, useSkills, useWorkspaces, actions }) {
228
+ function SkillImporterSection({ t, useSkills, useSkillIssues, useWorkspaces, actions }) {
229
229
  const skills = useSkills((value) => value);
230
+ const skillIssues = useSkillIssues((value) => value);
230
231
  const workspaces = useWorkspaces((value) => value);
231
- const workspace = (0, react.useMemo)(() => workspaces.items.find((item) => item.workspaceId === workspaces.recentWorkspaceId) ?? workspaces.items[0], [workspaces.items, workspaces.recentWorkspaceId]);
232
+ const [selectedWorkspaceId, setSelectedWorkspaceId] = (0, react.useState)(() => workspaces.recentWorkspaceId ?? workspaces.items[0]?.workspaceId);
233
+ (0, react.useEffect)(() => {
234
+ if (selectedWorkspaceId !== void 0 && workspaces.items.some((item) => item.workspaceId === selectedWorkspaceId)) return;
235
+ setSelectedWorkspaceId(workspaces.recentWorkspaceId ?? workspaces.items[0]?.workspaceId);
236
+ }, [
237
+ selectedWorkspaceId,
238
+ workspaces.items,
239
+ workspaces.recentWorkspaceId
240
+ ]);
241
+ const workspace = (0, react.useMemo)(() => workspaces.items.find((item) => item.workspaceId === selectedWorkspaceId), [selectedWorkspaceId, workspaces.items]);
232
242
  const [listState, setListState] = (0, react.useState)("idle");
233
243
  const [listError, setListError] = (0, react.useState)();
234
244
  const [listEpoch, setListEpoch] = (0, react.useState)(0);
235
- const [collapsedSources, setCollapsedSources] = (0, react.useState)(() => /* @__PURE__ */ new Set());
245
+ const [expandedGroups, setExpandedGroups] = (0, react.useState)(() => /* @__PURE__ */ new Set());
236
246
  (0, react.useEffect)(() => {
237
247
  let cancelled = false;
238
248
  setListState("loading");
@@ -249,7 +259,10 @@ window.__ModuleLoader__.load({
249
259
  };
250
260
  }, [listEpoch, actions]);
251
261
  const [mode, setMode] = (0, react.useState)("file");
252
- const [target, setTarget] = (0, react.useState)("project-agents");
262
+ const [target, setTarget] = (0, react.useState)("project-dsh");
263
+ (0, react.useEffect)(() => {
264
+ if (workspaces.items.length === 0 && target.startsWith("project-")) setTarget("user-dsh");
265
+ }, [target, workspaces.items.length]);
253
266
  const [fileDraft, setFileDraft] = (0, react.useState)();
254
267
  const [fileError, setFileError] = (0, react.useState)();
255
268
  const [url, setUrl] = (0, react.useState)("");
@@ -262,8 +275,41 @@ window.__ModuleLoader__.load({
262
275
  const [batchResults, setBatchResults] = (0, react.useState)();
263
276
  const [replaceNames, setReplaceNames] = (0, react.useState)(() => /* @__PURE__ */ new Set());
264
277
  const [pendingDelete, setPendingDelete] = (0, react.useState)();
278
+ const [locationOpen, setLocationOpen] = (0, react.useState)(false);
279
+ const [workspaceQuery, setWorkspaceQuery] = (0, react.useState)("");
280
+ const [draftTarget, setDraftTarget] = (0, react.useState)("project-dsh");
281
+ const [draftWorkspaceId, setDraftWorkspaceId] = (0, react.useState)();
282
+ const [updateState, setUpdateState] = (0, react.useState)("checking");
283
+ const [updateStatus, setUpdateStatus] = (0, react.useState)();
284
+ const [updateDialogOpen, setUpdateDialogOpen] = (0, react.useState)(false);
285
+ const [commandCopied, setCommandCopied] = (0, react.useState)(false);
265
286
  const [batchConfirmation, setBatchConfirmation] = (0, react.useState)();
266
287
  const fileInput = (0, react.useRef)(null);
288
+ const checkForUpdates = () => {
289
+ setUpdateState("checking");
290
+ actions.checkForUpdates().then((status) => {
291
+ setUpdateStatus(status);
292
+ setUpdateState(status.error === void 0 ? "ready" : "error");
293
+ }, () => setUpdateState("error"));
294
+ };
295
+ (0, react.useEffect)(() => {
296
+ let cancelled = false;
297
+ actions.checkForUpdates().then((status) => {
298
+ if (cancelled) return;
299
+ setUpdateStatus(status);
300
+ setUpdateState(status.error === void 0 ? "ready" : "error");
301
+ }, () => {
302
+ if (!cancelled) setUpdateState("error");
303
+ });
304
+ return () => {
305
+ cancelled = true;
306
+ };
307
+ }, [actions]);
308
+ const copyUpdateCommand = async () => {
309
+ if (updateStatus?.command === void 0) return;
310
+ await navigator.clipboard.writeText(updateStatus.command);
311
+ setCommandCopied(true);
312
+ };
267
313
  const onFilePicked = async (file) => {
268
314
  setFileDraft(void 0);
269
315
  setFileError(void 0);
@@ -314,7 +360,7 @@ window.__ModuleLoader__.load({
314
360
  if (await actions.deleteSkill({
315
361
  name: skill.name,
316
362
  source: skill.source,
317
- workspacePath: workspace?.path
363
+ workspacePath: skill.workspacePath
318
364
  })) {
319
365
  setMessage(t("deleted"));
320
366
  setPendingDelete(void 0);
@@ -454,7 +500,17 @@ window.__ModuleLoader__.load({
454
500
  setTarget(next);
455
501
  if (mode === "batch") resetBatchPreflight();
456
502
  };
457
- const canSubmit = busy || mode === "file" && fileDraft === void 0 || mode === "url" && (url.trim().length === 0 || !/^https?:\/\//i.test(url.trim())) || mode === "batch" && batchPath === void 0 || target !== "user" && workspace === void 0;
503
+ const chooseDestination = (nextTarget, nextWorkspaceId) => {
504
+ if (nextWorkspaceId !== void 0) setSelectedWorkspaceId(nextWorkspaceId);
505
+ changeTarget(nextTarget);
506
+ setLocationOpen(false);
507
+ };
508
+ const locationSummary = target === "project-dsh" ? t("locationSummaryProjectDsh", { title: workspace?.title ?? "" }) : target === "project-agents" ? t("locationSummaryProjectAgents", { title: workspace?.title ?? "" }) : target === "user-dsh" ? t("locationSummaryUserDsh") : t("locationSummaryUserAgents");
509
+ const filteredWorkspaces = workspaces.items.filter((item) => {
510
+ const query = workspaceQuery.trim().toLowerCase();
511
+ return query.length === 0 || item.title.toLowerCase().includes(query) || item.path.toLowerCase().includes(query);
512
+ });
513
+ const canSubmit = busy || mode === "file" && fileDraft === void 0 || mode === "url" && (url.trim().length === 0 || !/^https?:\/\//i.test(url.trim())) || mode === "batch" && batchPath === void 0 || target.startsWith("project-") && workspace === void 0;
458
514
  const runPrimaryAction = () => {
459
515
  if (mode !== "batch") {
460
516
  submit();
@@ -468,13 +524,33 @@ window.__ModuleLoader__.load({
468
524
  if (!skill.modelInvocable && skill.userInvocable) return t("userOnly");
469
525
  if (skill.modelInvocable && !skill.userInvocable) return t("modelOnly");
470
526
  };
471
- const toggleSource = (source) => {
472
- setCollapsedSources((current) => {
527
+ const toggleGroup = (groupKey) => {
528
+ setExpandedGroups((current) => {
473
529
  const next = new Set(current);
474
- if (!next.delete(source)) next.add(source);
530
+ if (!next.delete(groupKey)) next.add(groupKey);
475
531
  return next;
476
532
  });
477
533
  };
534
+ const skillGroups = [
535
+ ...workspaces.items.flatMap((item) => ["project-dsh", "project-agents"].map((source) => ({
536
+ key: `${item.workspaceId}:${source}`,
537
+ source,
538
+ workspacePath: item.path,
539
+ name: source === "project-dsh" ? t("targetProjectDsh", { title: item.title }) : t("targetProjectAgents", { title: item.title })
540
+ }))),
541
+ {
542
+ key: "user-dsh",
543
+ source: "user-dsh",
544
+ workspacePath: void 0,
545
+ name: t("targetUserDsh")
546
+ },
547
+ {
548
+ key: "user-agents",
549
+ source: "user-agents",
550
+ workspacePath: void 0,
551
+ name: t("targetUserAgents")
552
+ }
553
+ ];
478
554
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
479
555
  style: {
480
556
  display: "flex",
@@ -529,23 +605,33 @@ window.__ModuleLoader__.load({
529
605
  listError
530
606
  ]
531
607
  }) : null,
608
+ listState !== "error" && skillIssues.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
609
+ style: {
610
+ ...descriptionStyle,
611
+ color: "var(--dsw-alias-state-warn-label)"
612
+ },
613
+ children: [
614
+ t("installedPartial", { count: skillIssues.length }),
615
+ " ",
616
+ skillIssues[0]?.message
617
+ ]
618
+ }) : null,
532
619
  listState === "idle" && skills.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
533
620
  style: descriptionStyle,
534
621
  children: t("installedEmpty")
535
622
  }) : null,
536
623
  skills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
537
624
  style: skillGroupsStyle,
538
- children: ["project-agents", "user"].map((source) => {
539
- const rows = skills.filter((skill) => skill.source === source);
625
+ children: skillGroups.map((group) => {
626
+ const rows = skills.filter((skill) => skill.source === group.source && (!group.source.startsWith("project-") || skill.workspacePath === group.workspacePath));
540
627
  if (rows.length === 0) return null;
541
- const sourceName = source === "user" ? t("targetUser") : workspace === void 0 ? t("targetProjectNoWorkspace") : t("targetProjectAgents", { title: workspace.title });
542
- const open = !collapsedSources.has(source);
628
+ const open = expandedGroups.has(group.key);
543
629
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
544
630
  style: skillGroupCardStyle,
545
631
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
546
632
  type: "button",
547
633
  "aria-expanded": open,
548
- onClick: () => toggleSource(source),
634
+ onClick: () => toggleGroup(group.key),
549
635
  style: {
550
636
  ...skillGroupHeaderStyle,
551
637
  borderBottom: open ? skillGroupHeaderStyle.borderBottom : "none"
@@ -559,7 +645,7 @@ window.__ModuleLoader__.load({
559
645
  style: skillGroupIdentityStyle,
560
646
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
561
647
  style: skillGroupNameStyle,
562
- children: sourceName
648
+ children: group.name
563
649
  })
564
650
  }),
565
651
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -607,10 +693,10 @@ window.__ModuleLoader__.load({
607
693
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, {})
608
694
  })
609
695
  ]
610
- }, skill.source + ":" + skill.name);
696
+ }, skill.source + ":" + (skill.workspacePath ?? "") + ":" + skill.name);
611
697
  })
612
698
  }) : null]
613
- }, source);
699
+ }, group.key);
614
700
  })
615
701
  }) : null
616
702
  ]
@@ -1014,17 +1100,28 @@ window.__ModuleLoader__.load({
1014
1100
  style: fieldLabelStyle,
1015
1101
  children: t("targetLabel")
1016
1102
  }),
1017
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1018
- value: target,
1019
- onChange: (event) => changeTarget(event.target.value),
1020
- style: selectStyle,
1021
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1022
- value: "project-agents",
1023
- disabled: workspace === void 0,
1024
- children: workspace === void 0 ? t("targetProjectNoWorkspace") : t("targetProjectAgents", { title: workspace.title })
1025
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1026
- value: "user",
1027
- children: t("targetUser")
1103
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1104
+ type: "button",
1105
+ onClick: () => {
1106
+ setWorkspaceQuery("");
1107
+ setDraftTarget(target);
1108
+ setDraftWorkspaceId(selectedWorkspaceId);
1109
+ setLocationOpen(true);
1110
+ },
1111
+ style: locationSummaryStyle,
1112
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1113
+ style: {
1114
+ minWidth: 0,
1115
+ flex: 1,
1116
+ overflow: "hidden",
1117
+ textOverflow: "ellipsis",
1118
+ whiteSpace: "nowrap",
1119
+ textAlign: "left"
1120
+ },
1121
+ children: locationSummary
1122
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1123
+ style: secondaryButtonStyle,
1124
+ children: t("locationChange")
1028
1125
  })]
1029
1126
  }),
1030
1127
  workspace === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -1057,6 +1154,191 @@ window.__ModuleLoader__.load({
1057
1154
  ]
1058
1155
  })
1059
1156
  }),
1157
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1158
+ style: versionFooterStyle,
1159
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1160
+ style: versionCopyStyle,
1161
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1162
+ style: versionLabelStyle,
1163
+ children: updateStatus === void 0 ? t("versionChecking") : t("versionCurrent", { version: updateStatus.currentVersion })
1164
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1165
+ style: descriptionStyle,
1166
+ children: updateState === "checking" ? t("versionChecking") : updateState === "error" ? t("versionCheckFailed") : updateStatus?.updateAvailable === true ? t("versionAvailable", { version: updateStatus.latestVersion ?? "" }) : t("versionLatest")
1167
+ })]
1168
+ }), updateState === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1169
+ type: "button",
1170
+ onClick: checkForUpdates,
1171
+ style: pillStyle,
1172
+ children: t("versionRetry")
1173
+ }) : updateStatus?.updateAvailable === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1174
+ type: "button",
1175
+ onClick: () => {
1176
+ setCommandCopied(false);
1177
+ setUpdateDialogOpen(true);
1178
+ },
1179
+ style: updateButtonStyle,
1180
+ children: t("versionUpdate")
1181
+ }) : null]
1182
+ }),
1183
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1184
+ open: locationOpen,
1185
+ onClose: () => setLocationOpen(false),
1186
+ title: t("locationTitle"),
1187
+ closeLabel: t("close"),
1188
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1189
+ variant: "outline",
1190
+ onClick: () => setLocationOpen(false),
1191
+ children: t("cancel")
1192
+ }),
1193
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1194
+ style: locationPanelStyle,
1195
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1196
+ style: locationScopeStyle,
1197
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1198
+ type: "button",
1199
+ onClick: () => {
1200
+ if (!draftTarget.startsWith("project-")) setDraftTarget("project-dsh");
1201
+ },
1202
+ style: {
1203
+ ...locationScopeButtonStyle,
1204
+ ...draftTarget.startsWith("project-") ? locationScopeButtonActiveStyle : {}
1205
+ },
1206
+ children: t("locationProject")
1207
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1208
+ type: "button",
1209
+ onClick: () => {
1210
+ if (draftTarget.startsWith("project-")) setDraftTarget("user-dsh");
1211
+ },
1212
+ style: {
1213
+ ...locationScopeButtonStyle,
1214
+ ...!draftTarget.startsWith("project-") ? locationScopeButtonActiveStyle : {}
1215
+ },
1216
+ children: t("locationGlobal")
1217
+ })]
1218
+ }), draftTarget.startsWith("project-") ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1219
+ type: "search",
1220
+ value: workspaceQuery,
1221
+ onChange: (event) => setWorkspaceQuery(event.target.value),
1222
+ placeholder: t("workspaceSearch"),
1223
+ style: controlStyle
1224
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1225
+ style: locationWorkspaceListStyle,
1226
+ children: [filteredWorkspaces.map((item) => {
1227
+ const selected = item.workspaceId === draftWorkspaceId;
1228
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1229
+ style: {
1230
+ ...locationWorkspaceGroupStyle,
1231
+ ...selected ? locationWorkspaceActiveStyle : {}
1232
+ },
1233
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1234
+ type: "button",
1235
+ onClick: (event) => {
1236
+ const group = event.currentTarget.parentElement;
1237
+ setDraftWorkspaceId(selected ? void 0 : item.workspaceId);
1238
+ if (!selected) window.requestAnimationFrame(() => group?.scrollIntoView({
1239
+ block: "nearest",
1240
+ behavior: "smooth"
1241
+ }));
1242
+ },
1243
+ style: locationWorkspaceStyle,
1244
+ "aria-expanded": selected,
1245
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1246
+ style: {
1247
+ minWidth: 0,
1248
+ flex: 1,
1249
+ display: "flex",
1250
+ flexDirection: "column",
1251
+ gap: 2
1252
+ },
1253
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1254
+ style: skillNameStyle,
1255
+ children: item.title
1256
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1257
+ style: skillDescriptionStyle,
1258
+ children: item.path
1259
+ })]
1260
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1261
+ style: skillGroupChevronStyle,
1262
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronIcon, { open: selected })
1263
+ })]
1264
+ }), selected ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1265
+ style: locationExpandedRootsStyle,
1266
+ children: [
1267
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1268
+ style: locationRootHintStyle,
1269
+ children: t("chooseRootHint")
1270
+ }),
1271
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1272
+ type: "button",
1273
+ onClick: () => chooseDestination("project-dsh", item.workspaceId),
1274
+ style: locationOptionStyle,
1275
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1276
+ style: locationOptionCopyStyle,
1277
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("rootProjectDsh") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
1278
+ style: descriptionStyle,
1279
+ children: ".dsh/skills"
1280
+ })]
1281
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1282
+ style: priorityHighStyle,
1283
+ children: t("priorityHighest")
1284
+ })]
1285
+ }),
1286
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1287
+ type: "button",
1288
+ onClick: () => chooseDestination("project-agents", item.workspaceId),
1289
+ style: locationOptionStyle,
1290
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1291
+ style: locationOptionCopyStyle,
1292
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("rootProjectAgents") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
1293
+ style: descriptionStyle,
1294
+ children: ".agents/skills"
1295
+ })]
1296
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1297
+ style: priorityMediumStyle,
1298
+ children: t("priorityHigher")
1299
+ })]
1300
+ })
1301
+ ]
1302
+ }) : null]
1303
+ }, item.workspaceId);
1304
+ }), filteredWorkspaces.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1305
+ style: descriptionStyle,
1306
+ children: t("workspaceNoMatch")
1307
+ }) : null]
1308
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1309
+ style: locationRootsStyle,
1310
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1311
+ type: "button",
1312
+ onClick: () => chooseDestination("user-dsh"),
1313
+ style: locationOptionStyle,
1314
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1315
+ style: locationOptionCopyStyle,
1316
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("rootUserDsh") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
1317
+ style: descriptionStyle,
1318
+ children: "~/.dsh/skills"
1319
+ })]
1320
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1321
+ style: priorityLowStyle,
1322
+ children: t("priorityLower")
1323
+ })]
1324
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1325
+ type: "button",
1326
+ onClick: () => chooseDestination("user-agents"),
1327
+ style: locationOptionStyle,
1328
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1329
+ style: locationOptionCopyStyle,
1330
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("rootUserAgents") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
1331
+ style: descriptionStyle,
1332
+ children: "~/.agents/skills"
1333
+ })]
1334
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1335
+ style: priorityLowStyle,
1336
+ children: t("priorityLowest")
1337
+ })]
1338
+ })]
1339
+ })]
1340
+ })
1341
+ }),
1060
1342
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1061
1343
  open: pendingDelete !== void 0,
1062
1344
  onClose: () => {
@@ -1100,6 +1382,34 @@ window.__ModuleLoader__.load({
1100
1382
  },
1101
1383
  children: busy ? t("batchWorking") : t("batchCommit")
1102
1384
  })] })
1385
+ }),
1386
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1387
+ open: updateDialogOpen,
1388
+ onClose: () => setUpdateDialogOpen(false),
1389
+ title: t("updateTitle"),
1390
+ closeLabel: t("close"),
1391
+ description: t("updateDescription"),
1392
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1393
+ variant: "outline",
1394
+ onClick: () => setUpdateDialogOpen(false),
1395
+ children: t("cancel")
1396
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1397
+ variant: "primary",
1398
+ onClick: () => {
1399
+ copyUpdateCommand();
1400
+ },
1401
+ children: commandCopied ? t("commandCopied") : t("copyCommand")
1402
+ })] }),
1403
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1404
+ style: updateDialogBodyStyle,
1405
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
1406
+ style: updateCommandStyle,
1407
+ children: updateStatus?.command
1408
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1409
+ style: descriptionStyle,
1410
+ children: t("restartHint")
1411
+ })]
1412
+ })
1103
1413
  })
1104
1414
  ]
1105
1415
  });
@@ -1128,6 +1438,58 @@ window.__ModuleLoader__.load({
1128
1438
  lineHeight: "18px",
1129
1439
  color: "var(--dsw-alias-label-caption)"
1130
1440
  };
1441
+ const versionFooterStyle = {
1442
+ minHeight: 58,
1443
+ padding: "12px 0",
1444
+ display: "flex",
1445
+ alignItems: "center",
1446
+ gap: 12,
1447
+ borderTop: "1px solid var(--dsw-alias-border-l2)"
1448
+ };
1449
+ const versionCopyStyle = {
1450
+ flex: 1,
1451
+ minWidth: 0,
1452
+ display: "flex",
1453
+ flexDirection: "column",
1454
+ gap: 2
1455
+ };
1456
+ const versionLabelStyle = {
1457
+ fontSize: 12,
1458
+ lineHeight: "18px",
1459
+ fontWeight: 500,
1460
+ color: "var(--dsw-alias-label-secondary)"
1461
+ };
1462
+ const updateButtonStyle = {
1463
+ display: "inline-flex",
1464
+ alignItems: "center",
1465
+ height: 28,
1466
+ padding: "0 12px",
1467
+ border: "1px solid var(--dsw-alias-interactive-border-primary)",
1468
+ borderRadius: 14,
1469
+ background: "transparent",
1470
+ font: "inherit",
1471
+ fontSize: 13,
1472
+ borderColor: "var(--dsw-alias-interactive-border-primary)",
1473
+ color: "var(--dsw-alias-interactive-label-primary)",
1474
+ cursor: "pointer"
1475
+ };
1476
+ const updateDialogBodyStyle = {
1477
+ display: "flex",
1478
+ flexDirection: "column",
1479
+ gap: 10
1480
+ };
1481
+ const updateCommandStyle = {
1482
+ display: "block",
1483
+ padding: "10px 12px",
1484
+ border: "1px solid var(--dsw-alias-border-l2)",
1485
+ borderRadius: 8,
1486
+ background: "var(--dsw-alias-bg-module-platform)",
1487
+ color: "var(--dsw-alias-label-primary)",
1488
+ fontSize: 12,
1489
+ lineHeight: "18px",
1490
+ whiteSpace: "pre-wrap",
1491
+ wordBreak: "break-all"
1492
+ };
1131
1493
  const pillStyle = {
1132
1494
  display: "inline-flex",
1133
1495
  alignItems: "center",
@@ -1305,18 +1667,7 @@ window.__ModuleLoader__.load({
1305
1667
  color: "var(--dsw-alias-label-primary)",
1306
1668
  outline: "none"
1307
1669
  };
1308
- /** Select variant: replace the OS arrow with the shared 12px chevron inset
1309
- * (same treatment as the composer chips / models page selects). */
1310
- const selectStyle = {
1311
- ...controlStyle,
1312
- appearance: "none",
1313
- paddingRight: 32,
1314
- backgroundImage: "url(\"data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2712%27 height=%2712%27 viewBox=%270 0 12 12%27 fill=%27none%27%3E%3Cpath d=%27M3 4.5L6 7.5L9 4.5%27 stroke=%27%2381858C%27 stroke-width=%271.5%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27/%3E%3C/svg%3E\")",
1315
- backgroundRepeat: "no-repeat",
1316
- backgroundPosition: "right 12px center",
1317
- backgroundSize: "12px 12px",
1318
- cursor: "pointer"
1319
- };
1670
+ ({ ...controlStyle });
1320
1671
  const entryGridStyle = {
1321
1672
  display: "grid",
1322
1673
  gridTemplateColumns: "repeat(3, minmax(0, 1fr))",
@@ -1417,6 +1768,127 @@ window.__ModuleLoader__.load({
1417
1768
  fontSize: 12,
1418
1769
  lineHeight: "18px"
1419
1770
  };
1771
+ const locationSummaryStyle = {
1772
+ ...controlStyle,
1773
+ display: "flex",
1774
+ alignItems: "center",
1775
+ gap: 10,
1776
+ cursor: "pointer"
1777
+ };
1778
+ const locationPanelStyle = {
1779
+ display: "flex",
1780
+ flexDirection: "column",
1781
+ gap: 14,
1782
+ minWidth: 0
1783
+ };
1784
+ const locationScopeStyle = {
1785
+ display: "grid",
1786
+ gridTemplateColumns: "1fr 1fr",
1787
+ gap: 4,
1788
+ padding: 4,
1789
+ borderRadius: 10,
1790
+ background: "var(--dsw-alias-bg-module-platform)"
1791
+ };
1792
+ const locationScopeButtonStyle = {
1793
+ border: "none",
1794
+ borderRadius: 7,
1795
+ padding: "8px 12px",
1796
+ background: "transparent",
1797
+ color: "var(--dsw-alias-label-secondary)",
1798
+ cursor: "pointer",
1799
+ fontWeight: 500
1800
+ };
1801
+ const locationScopeButtonActiveStyle = {
1802
+ background: "var(--dsw-alias-bg-layer-1)",
1803
+ color: "var(--dsw-alias-label-primary)",
1804
+ boxShadow: "0 1px 3px rgba(0,0,0,.08)"
1805
+ };
1806
+ const locationWorkspaceListStyle = {
1807
+ display: "flex",
1808
+ flexDirection: "column",
1809
+ gap: 4,
1810
+ maxHeight: "min(440px, 52vh)",
1811
+ overflowY: "auto",
1812
+ scrollPadding: "8px 0"
1813
+ };
1814
+ const locationWorkspaceGroupStyle = {
1815
+ overflow: "hidden",
1816
+ borderRadius: 10,
1817
+ transition: "background 140ms ease"
1818
+ };
1819
+ const locationWorkspaceStyle = {
1820
+ width: "100%",
1821
+ display: "flex",
1822
+ alignItems: "center",
1823
+ gap: 8,
1824
+ border: "none",
1825
+ borderRadius: 8,
1826
+ padding: "9px 10px",
1827
+ background: "transparent",
1828
+ cursor: "pointer",
1829
+ textAlign: "left"
1830
+ };
1831
+ const locationWorkspaceActiveStyle = { background: "var(--dsw-alias-bg-module-platform)" };
1832
+ const locationExpandedRootsStyle = {
1833
+ display: "flex",
1834
+ flexDirection: "column",
1835
+ gap: 8,
1836
+ padding: "2px 10px 10px 18px"
1837
+ };
1838
+ const locationRootHintStyle = {
1839
+ fontSize: 11,
1840
+ lineHeight: "16px",
1841
+ fontWeight: 500,
1842
+ color: "var(--dsw-alias-label-tertiary)"
1843
+ };
1844
+ const locationRootsStyle = {
1845
+ display: "flex",
1846
+ flexDirection: "column",
1847
+ gap: 8
1848
+ };
1849
+ const locationOptionStyle = {
1850
+ display: "flex",
1851
+ alignItems: "center",
1852
+ gap: 12,
1853
+ width: "100%",
1854
+ border: "1px solid var(--dsw-alias-border-l2)",
1855
+ borderRadius: 10,
1856
+ padding: "10px 12px",
1857
+ background: "var(--dsw-alias-bg-layer-1)",
1858
+ color: "var(--dsw-alias-label-primary)",
1859
+ cursor: "pointer",
1860
+ textAlign: "left"
1861
+ };
1862
+ const locationOptionCopyStyle = {
1863
+ minWidth: 0,
1864
+ flex: 1,
1865
+ display: "flex",
1866
+ flexDirection: "column",
1867
+ gap: 2
1868
+ };
1869
+ const priorityBaseStyle = {
1870
+ flex: "none",
1871
+ borderRadius: 999,
1872
+ padding: "2px 7px",
1873
+ fontSize: 11,
1874
+ lineHeight: "16px",
1875
+ fontWeight: 600
1876
+ };
1877
+ const priorityHighStyle = {
1878
+ ...priorityBaseStyle,
1879
+ color: "var(--dsw-alias-state-success-primary)",
1880
+ background: "var(--dsw-alias-bg-module-platform)"
1881
+ };
1882
+ const priorityMediumStyle = {
1883
+ ...priorityBaseStyle,
1884
+ color: "var(--dsw-alias-label-primary)",
1885
+ background: "var(--dsw-alias-bg-module-platform)"
1886
+ };
1887
+ const priorityLowStyle = {
1888
+ ...priorityBaseStyle,
1889
+ color: "var(--dsw-alias-label-secondary)",
1890
+ background: "var(--dsw-alias-bg-module-platform)"
1891
+ };
1420
1892
  const previewStyle = {
1421
1893
  display: "flex",
1422
1894
  flexDirection: "column",
@@ -1640,18 +2112,43 @@ window.__ModuleLoader__.load({
1640
2112
  }
1641
2113
  }
1642
2114
  /** One row of the composer skill picker. */
1643
- function SkillsPicker({ t, sessionId, useSkills, useInput, inputActions, actions }) {
1644
- const skills = useSkills((value) => value);
2115
+ function SkillsPicker({ t, sessionId, useInput, inputActions, actions }) {
1645
2116
  const input = useInput((value) => value.draft);
2117
+ const [skills, setSkills] = (0, react.useState)([]);
1646
2118
  const [open, setOpen] = (0, react.useState)(false);
2119
+ const [loading, setLoading] = (0, react.useState)(true);
1647
2120
  const [query, setQuery] = (0, react.useState)("");
1648
2121
  const rootRef = (0, react.useRef)(null);
1649
2122
  const draftRef = (0, react.useRef)(input);
1650
2123
  const skillNamesRef = (0, react.useRef)([]);
1651
2124
  const caretFrameRef = (0, react.useRef)();
1652
2125
  (0, react.useEffect)(() => {
1653
- actions.refreshSkills().catch(() => {});
1654
- }, [actions]);
2126
+ let cancelled = false;
2127
+ const refresh = (showLoading) => {
2128
+ if (showLoading) setLoading(true);
2129
+ actions.refreshSkillsForSession(sessionId).then((rows) => {
2130
+ if (!cancelled) {
2131
+ setSkills(rows);
2132
+ setLoading(false);
2133
+ }
2134
+ }, () => {
2135
+ if (!cancelled) {
2136
+ setSkills([]);
2137
+ setLoading(false);
2138
+ }
2139
+ });
2140
+ };
2141
+ refresh(true);
2142
+ const timer = open ? window.setInterval(() => refresh(false), 2e3) : void 0;
2143
+ return () => {
2144
+ cancelled = true;
2145
+ if (timer !== void 0) window.clearInterval(timer);
2146
+ };
2147
+ }, [
2148
+ actions,
2149
+ sessionId,
2150
+ open
2151
+ ]);
1655
2152
  (0, react.useEffect)(() => {
1656
2153
  if (!open) return;
1657
2154
  const onDown = (event) => {
@@ -1727,7 +2224,6 @@ window.__ModuleLoader__.load({
1727
2224
  if (caretFrameRef.current !== void 0) window.cancelAnimationFrame(caretFrameRef.current);
1728
2225
  };
1729
2226
  }, [inputActions]);
1730
- if (usable.length === 0) return null;
1731
2227
  const filtered = query.trim().length === 0 ? usable : usable.filter((skill) => skill.name.toLowerCase().includes(query.trim().toLowerCase()));
1732
2228
  const onSelect = (name) => {
1733
2229
  const prefix = `/${name} `;
@@ -1743,7 +2239,10 @@ window.__ModuleLoader__.load({
1743
2239
  type: "button",
1744
2240
  "aria-label": t("pickerLabel"),
1745
2241
  "aria-expanded": open,
1746
- onClick: () => setOpen((value) => !value),
2242
+ onClick: () => {
2243
+ if (!open) setLoading(true);
2244
+ setOpen((value) => !value);
2245
+ },
1747
2246
  style: triggerStyle,
1748
2247
  children: [
1749
2248
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -1775,7 +2274,10 @@ window.__ModuleLoader__.load({
1775
2274
  placeholder: t("pickerSearchPlaceholder"),
1776
2275
  style: searchStyle
1777
2276
  }),
1778
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
2277
+ loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2278
+ style: emptyStyle,
2279
+ children: t("pickerLoading")
2280
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
1779
2281
  style: listStyle,
1780
2282
  children: filtered.map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1781
2283
  type: "button",
@@ -1785,13 +2287,19 @@ window.__ModuleLoader__.load({
1785
2287
  style: avatarStyle,
1786
2288
  "aria-hidden": "true",
1787
2289
  children: skill.name.charAt(0).toUpperCase()
1788
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1789
- style: rowLabelStyle,
1790
- children: skill.name
2290
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2291
+ style: rowCopyStyle,
2292
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2293
+ style: rowLabelStyle,
2294
+ children: skill.name
2295
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2296
+ style: rowDetailStyle,
2297
+ children: skill.source === "project-dsh" ? t("pickerSourceProjectDsh") : skill.source === "project-agents" ? t("pickerSourceProjectAgents") : skill.source === "user-dsh" ? t("pickerSourceUserDsh") : t("pickerSourceUserAgents")
2298
+ })]
1791
2299
  })]
1792
2300
  }) }, skill.name))
1793
2301
  }),
1794
- filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2302
+ !loading && filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1795
2303
  style: emptyStyle,
1796
2304
  children: t("pickerEmpty")
1797
2305
  }) : null
@@ -1906,12 +2414,41 @@ window.__ModuleLoader__.load({
1906
2414
  minWidth: 0,
1907
2415
  overflow: "hidden"
1908
2416
  };
2417
+ const rowCopyStyle = {
2418
+ display: "flex",
2419
+ flexDirection: "column",
2420
+ minWidth: 0,
2421
+ flex: 1,
2422
+ lineHeight: "18px"
2423
+ };
2424
+ const rowDetailStyle = {
2425
+ overflow: "hidden",
2426
+ textOverflow: "ellipsis",
2427
+ whiteSpace: "nowrap",
2428
+ color: "var(--dsw-alias-label-caption)",
2429
+ fontSize: 11,
2430
+ lineHeight: "16px"
2431
+ };
1909
2432
  const emptyStyle = {
1910
2433
  padding: "8px 10px",
1911
2434
  fontSize: 12,
1912
2435
  color: "var(--dsw-alias-label-caption)"
1913
2436
  };
1914
2437
  //#endregion
2438
+ //#region src/client/effectiveSkills.ts
2439
+ const SOURCE_RANK = {
2440
+ "project-dsh": 100,
2441
+ "project-agents": 200,
2442
+ "user-dsh": 400,
2443
+ "user-agents": 500
2444
+ };
2445
+ /** Mirror DSH's name-based winner selection for one session-scoped catalog. */
2446
+ function effectiveSkills(rows) {
2447
+ const winners = /* @__PURE__ */ new Map();
2448
+ for (const row of [...rows].sort((a, b) => SOURCE_RANK[a.source] - SOURCE_RANK[b.source])) if (!winners.has(row.name)) winners.set(row.name, row);
2449
+ return [...winners.values()].sort((a, b) => a.name.localeCompare(b.name));
2450
+ }
2451
+ //#endregion
1915
2452
  //#region src/client/locales.ts
1916
2453
  /**
1917
2454
  * Locale dictionaries for the skill importer settings section.
@@ -1928,7 +2465,12 @@ window.__ModuleLoader__.load({
1928
2465
  pickerLabel: "技能",
1929
2466
  pickerPlaceholder: "技能",
1930
2467
  pickerSearchPlaceholder: "筛选技能…",
2468
+ pickerLoading: "正在同步当前项目技能…",
1931
2469
  pickerEmpty: "没有匹配的技能",
2470
+ pickerSourceProjectDsh: "当前项目 · .dsh",
2471
+ pickerSourceProjectAgents: "当前项目 · .agents",
2472
+ pickerSourceUserDsh: "全局 · .dsh",
2473
+ pickerSourceUserAgents: "全局 · .agents",
1932
2474
  commandDescription: "选择技能:弹出技能列表,选中后填入 /<名称>",
1933
2475
  entryTitle: "技能入口",
1934
2476
  entryDescription: "通过输入框工具行的技能下拉或 /skills 命令选择技能;也可直接输入 /<名称>。选中的技能会高亮并注入指令。",
@@ -1944,6 +2486,7 @@ window.__ModuleLoader__.load({
1944
2486
  deleteFailed: "删除失败",
1945
2487
  deleted: "已删除。",
1946
2488
  installedFailed: "无法读取技能列表",
2489
+ installedPartial: "有 {count} 个技能目录暂时无法读取,其他技能已正常显示。",
1947
2490
  userOnly: "仅斜杠可用",
1948
2491
  modelOnly: "仅模型可用",
1949
2492
  importTitle: "导入技能",
@@ -1994,15 +2537,48 @@ window.__ModuleLoader__.load({
1994
2537
  batchStatusSkipped: "已跳过",
1995
2538
  batchStatusError: "失败",
1996
2539
  targetLabel: "安装到",
1997
- targetUser: "全局技能(~/.dsh/skills)",
1998
- targetProjectAgents: "项目技能({title} · .agents/skills)",
2540
+ targetProjectDsh: "DSH 项目技能({title})",
2541
+ targetProjectAgents: "通用项目技能({title})",
2542
+ targetUserDsh: "DSH 全局技能",
2543
+ targetUserAgents: "通用全局技能",
2544
+ locationChange: "更改",
2545
+ locationTitle: "选择安装位置",
2546
+ locationProject: "项目技能",
2547
+ locationGlobal: "全局技能",
2548
+ workspaceSearch: "搜索工作区…",
2549
+ workspaceNoMatch: "没有匹配的工作区",
2550
+ chooseRootHint: "选择技能目录",
2551
+ rootProjectDsh: "DSH 项目技能",
2552
+ rootProjectAgents: "通用项目技能",
2553
+ rootUserDsh: "DSH 全局技能",
2554
+ rootUserAgents: "通用全局技能",
2555
+ priorityHighest: "最高",
2556
+ priorityHigher: "较高",
2557
+ priorityLower: "较低",
2558
+ priorityLowest: "最低",
2559
+ locationSummaryProjectDsh: "项目技能 · {title} · .dsh/skills",
2560
+ locationSummaryProjectAgents: "项目技能 · {title} · .agents/skills",
2561
+ locationSummaryUserDsh: "全局技能 · ~/.dsh/skills",
2562
+ locationSummaryUserAgents: "全局技能 · ~/.agents/skills",
1999
2563
  targetProjectNoWorkspace: "项目技能(需先添加工作区)",
2000
2564
  noWorkspace: "未注册工作区:请先在会话页选择/创建工作区,或改用用户技能目录。",
2001
2565
  import: "导入",
2002
2566
  importing: "导入中…",
2003
2567
  imported: "已写入:",
2004
2568
  importFailed: "导入失败",
2005
- hostUnreachable: "无法连接 dsh 服务(插件路由未生效)。请确认插件已安装,并重启 dsh web 后刷新页面。"
2569
+ hostUnreachable: "无法连接 dsh 服务(插件路由未生效)。请确认插件已安装,并重启 dsh web 后刷新页面。",
2570
+ versionChecking: "正在检查更新…",
2571
+ versionCurrent: "当前版本 v{version}",
2572
+ versionLatest: "已是最新版本",
2573
+ versionAvailable: "发现新版本 v{version}",
2574
+ versionCheckFailed: "暂时无法检查更新",
2575
+ versionRetry: "重试",
2576
+ versionUpdate: "更新",
2577
+ updateTitle: "更新 dsh-skill-importer",
2578
+ updateDescription: "复制下面的精确版本命令,在终端运行后重启 dsh web。",
2579
+ copyCommand: "复制更新命令",
2580
+ commandCopied: "已复制",
2581
+ restartHint: "更新完成后需要重启 dsh web 才会生效。"
2006
2582
  };
2007
2583
  /** English dictionary, checked complete against the zh key set. */
2008
2584
  const en = {
@@ -2010,7 +2586,12 @@ window.__ModuleLoader__.load({
2010
2586
  pickerLabel: "Skills",
2011
2587
  pickerPlaceholder: "Skills",
2012
2588
  pickerSearchPlaceholder: "Filter skills…",
2589
+ pickerLoading: "Syncing skills for the current project…",
2013
2590
  pickerEmpty: "No matching skills",
2591
+ pickerSourceProjectDsh: "Current project · .dsh",
2592
+ pickerSourceProjectAgents: "Current project · .agents",
2593
+ pickerSourceUserDsh: "Global · .dsh",
2594
+ pickerSourceUserAgents: "Global · .agents",
2014
2595
  commandDescription: "Pick a skill: opens a popup list; selecting fills /<name>",
2015
2596
  entryTitle: "Skill entry points",
2016
2597
  entryDescription: "Pick a skill from the composer tool-row dropdown or the /skills command; or type /<name> directly. The picked skill is highlighted and its instructions are injected.",
@@ -2026,6 +2607,7 @@ window.__ModuleLoader__.load({
2026
2607
  deleteFailed: "Failed to delete",
2027
2608
  deleted: "Deleted.",
2028
2609
  installedFailed: "Unable to read the skill list",
2610
+ installedPartial: "{count} skill directories are temporarily unreadable; other skills are still shown.",
2029
2611
  userOnly: "Slash only",
2030
2612
  modelOnly: "Model only",
2031
2613
  importTitle: "Import a skill",
@@ -2076,15 +2658,48 @@ window.__ModuleLoader__.load({
2076
2658
  batchStatusSkipped: "Skipped",
2077
2659
  batchStatusError: "Failed",
2078
2660
  targetLabel: "Install into",
2079
- targetUser: "Global skills (~/.dsh/skills)",
2080
- targetProjectAgents: "Project skills ({title} · .agents/skills)",
2661
+ targetProjectDsh: "DSH project skills ({title})",
2662
+ targetProjectAgents: "Shared project skills ({title})",
2663
+ targetUserDsh: "DSH global skills",
2664
+ targetUserAgents: "Shared global skills",
2665
+ locationChange: "Change",
2666
+ locationTitle: "Choose install location",
2667
+ locationProject: "Project skills",
2668
+ locationGlobal: "Global skills",
2669
+ workspaceSearch: "Search workspaces…",
2670
+ workspaceNoMatch: "No matching workspaces",
2671
+ chooseRootHint: "Choose a skill directory",
2672
+ rootProjectDsh: "DSH project skills",
2673
+ rootProjectAgents: "Shared project skills",
2674
+ rootUserDsh: "DSH global skills",
2675
+ rootUserAgents: "Shared global skills",
2676
+ priorityHighest: "Highest",
2677
+ priorityHigher: "Higher",
2678
+ priorityLower: "Lower",
2679
+ priorityLowest: "Lowest",
2680
+ locationSummaryProjectDsh: "Project · {title} · .dsh/skills",
2681
+ locationSummaryProjectAgents: "Project · {title} · .agents/skills",
2682
+ locationSummaryUserDsh: "Global · ~/.dsh/skills",
2683
+ locationSummaryUserAgents: "Global · ~/.agents/skills",
2081
2684
  targetProjectNoWorkspace: "Project skills (add a workspace first)",
2082
2685
  noWorkspace: "No registered workspace: create/select one in a session, or use the user skill directory.",
2083
2686
  import: "Import",
2084
2687
  importing: "Importing…",
2085
2688
  imported: "Written to: ",
2086
2689
  importFailed: "Import failed",
2087
- hostUnreachable: "Cannot reach the dsh service (plugin routes are not active). Make sure the plugin is installed, restart dsh web, and refresh the page."
2690
+ hostUnreachable: "Cannot reach the dsh service (plugin routes are not active). Make sure the plugin is installed, restart dsh web, and refresh the page.",
2691
+ versionChecking: "Checking for updates…",
2692
+ versionCurrent: "Current version v{version}",
2693
+ versionLatest: "Up to date",
2694
+ versionAvailable: "Version v{version} is available",
2695
+ versionCheckFailed: "Unable to check for updates",
2696
+ versionRetry: "Retry",
2697
+ versionUpdate: "Update",
2698
+ updateTitle: "Update dsh-skill-importer",
2699
+ updateDescription: "Copy this exact-version command, run it in a terminal, then restart dsh web.",
2700
+ copyCommand: "Copy update command",
2701
+ commandCopied: "Copied",
2702
+ restartHint: "Restart dsh web after the update for it to take effect."
2088
2703
  };
2089
2704
  //#endregion
2090
2705
  //#region src/client/index.ts
@@ -2101,7 +2716,8 @@ window.__ModuleLoader__.load({
2101
2716
  importUrl: "/skill-importer/import-url",
2102
2717
  delete: "/skill-importer/delete",
2103
2718
  batchScan: "/skill-importer/batch/scan",
2104
- batchCommit: "/skill-importer/batch/commit"
2719
+ batchCommit: "/skill-importer/batch/commit",
2720
+ updateStatus: "/skill-importer/update-status"
2105
2721
  };
2106
2722
  /** Fetch one route and unwrap the JSON envelope; throws on transport/HTTP errors. */
2107
2723
  async function call(path, body) {
@@ -2134,7 +2750,10 @@ window.__ModuleLoader__.load({
2134
2750
  en
2135
2751
  }), "dsh-skill-importer: dictionaries");
2136
2752
  const t = ctx.locale.bind(NS);
2753
+ const sourceLabel = (source) => source === "project-dsh" ? t("pickerSourceProjectDsh") : source === "project-agents" ? t("pickerSourceProjectAgents") : source === "user-dsh" ? t("pickerSourceUserDsh") : t("pickerSourceUserAgents");
2137
2754
  let catalog = [];
2755
+ let issues = [];
2756
+ const sessionCatalogs = /* @__PURE__ */ new Map();
2138
2757
  const listeners = /* @__PURE__ */ new Set();
2139
2758
  const notify = () => {
2140
2759
  for (const listener of [...listeners]) try {
@@ -2149,7 +2768,7 @@ window.__ModuleLoader__.load({
2149
2768
  skillsApi.list = ((_request, _signal) => {
2150
2769
  return Promise.reject(/* @__PURE__ */ new Error("skill group hidden: use the composer skill picker or /skills"));
2151
2770
  });
2152
- const usableNames = () => catalog.filter((skill) => skill.userInvocable).map((skill) => skill.name);
2771
+ const usableNames = () => [...new Set([...sessionCatalogs.values()].flat().filter((skill) => skill.userInvocable).map((skill) => skill.name))];
2153
2772
  ctx.effect(() => {
2154
2773
  return ctx.get("inputTriggers").registerSource({
2155
2774
  trigger: "/",
@@ -2181,10 +2800,10 @@ window.__ModuleLoader__.load({
2181
2800
  available: () => true,
2182
2801
  ui: {
2183
2802
  kind: "popupSelect",
2184
- async options(_session, _signal) {
2185
- return catalog.filter((skill) => skill.userInvocable).map((skill) => ({
2803
+ async options(session, _signal) {
2804
+ return (await refreshSkillsForSession(session.sessionId)).filter((skill) => skill.userInvocable).map((skill) => ({
2186
2805
  id: skill.name,
2187
- label: skill.name
2806
+ label: `${skill.name} · ${sourceLabel(skill.source)}`
2188
2807
  }));
2189
2808
  },
2190
2809
  onSelect(option, session) {
@@ -2204,11 +2823,20 @@ window.__ModuleLoader__.load({
2204
2823
  }
2205
2824
  });
2206
2825
  }, "dsh-skill-importer: /skills popupSelect command");
2207
- const refreshSkills = async () => {
2208
- catalog = (await call(ROUTES.list)).skills;
2826
+ const refreshSkills = async (workspacePath) => {
2827
+ const result = await call(workspacePath === void 0 ? ROUTES.list : `${ROUTES.list}?workspacePath=${encodeURIComponent(workspacePath)}`);
2828
+ catalog = result.skills;
2829
+ issues = result.issues;
2209
2830
  notify();
2210
2831
  return catalog;
2211
2832
  };
2833
+ const refreshSkillsForSession = async (sessionId) => {
2834
+ const cwd = sessions.list.getSnapshot().byId[sessionId]?.cwd;
2835
+ const effective = effectiveSkills((await call(cwd === void 0 ? `${ROUTES.list}?scope=global` : `${ROUTES.list}?workspacePath=${encodeURIComponent(cwd)}`)).skills);
2836
+ sessionCatalogs.set(sessionId, effective);
2837
+ notify();
2838
+ return effective;
2839
+ };
2212
2840
  const importSkill = async (request) => {
2213
2841
  return (await call(ROUTES.import, request)).path;
2214
2842
  };
@@ -2225,24 +2853,38 @@ window.__ModuleLoader__.load({
2225
2853
  };
2226
2854
  const scanBatch = (request) => call(ROUTES.batchScan, request);
2227
2855
  const commitBatch = (request) => call(ROUTES.batchCommit, request);
2856
+ const checkForUpdates = () => call(ROUTES.updateStatus);
2228
2857
  const face = () => ({
2229
- hooks: { skills: {
2230
- getSnapshot: () => catalog,
2231
- subscribe: (listener) => {
2232
- listeners.add(listener);
2233
- return () => {
2234
- listeners.delete(listener);
2235
- };
2858
+ hooks: {
2859
+ skills: {
2860
+ getSnapshot: () => catalog,
2861
+ subscribe: (listener) => {
2862
+ listeners.add(listener);
2863
+ return () => {
2864
+ listeners.delete(listener);
2865
+ };
2866
+ }
2867
+ },
2868
+ skillIssues: {
2869
+ getSnapshot: () => issues,
2870
+ subscribe: (listener) => {
2871
+ listeners.add(listener);
2872
+ return () => {
2873
+ listeners.delete(listener);
2874
+ };
2875
+ }
2236
2876
  }
2237
- } },
2877
+ },
2238
2878
  actions: {
2239
2879
  refreshSkills,
2880
+ refreshSkillsForSession,
2240
2881
  importSkill,
2241
2882
  importUrl,
2242
2883
  deleteSkill,
2243
2884
  pickDirectory,
2244
2885
  scanBatch,
2245
- commitBatch
2886
+ commitBatch,
2887
+ checkForUpdates
2246
2888
  }
2247
2889
  });
2248
2890
  ctx.slots.inject("settings.section", () => ctx.slots.register({