@morehao/dsh-skills-console 0.3.8 → 0.4.0

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/client.js CHANGED
@@ -86,18 +86,19 @@ var DICT = {
86
86
  "action.unsubscribe": "取消订阅",
87
87
  "action.view": "查看",
88
88
  "action.hide": "收起",
89
- "action.links": "接入设置",
89
+ "action.exports": "导出设置",
90
90
  "action.install": "安装",
91
91
  "action.expand": "展开",
92
92
  "action.collapse": "收起",
93
93
  "action.adopt": "接管",
94
94
  "action.save": "保存",
95
95
  "action.cancel": "取消",
96
+ "action.close": "关闭",
96
97
  "action.choose": "选择目录",
97
98
  "action.retry": "重新加载",
98
99
  "view.flat": "平铺",
99
100
  "view.agent": "按 Agent",
100
- autoLink: "自动接入",
101
+ exportDefault: "新安装时自动导出",
101
102
  "settings.title": "插件设置",
102
103
  "placement.label": "侧边栏入口",
103
104
  "placement.effect": "切换后立即生效",
@@ -123,23 +124,20 @@ var DICT = {
123
124
  "toast.checked": "检查完成",
124
125
  "mode.on": "启用",
125
126
  "mode.off": "停用",
126
- "mode.offHint": "停用中:DSH 看不到它,但已接入的其他 Agent 照常可用",
127
- "links.dshGlobal": "dsh(全局)",
128
- "links.required": "必选",
129
- "links.scope": "在 DSH 里的作用范围",
130
- "links.scopeGlobal": "全局(默认)",
131
- "links.scopeProjects": "仅以下项目",
132
- "links.targets": "接入到哪些 Agent",
133
- "links.detectedHint": "只显示本机已安装(目录存在)的 Agent;已删除的会从列表消失,旧接入记录保留但不展示。",
134
- "links.canonical": "技能本体:{path};下面列的是各 Agent 的接入目录(软链目标)。",
135
- "links.workspaces": "DSH 工作区",
136
- "links.noWorkspaces": "还没有 DSH 工作区,可以点下面「选择其他目录」",
137
- "links.pickDirectory": "选择其他目录…",
138
- "links.selected": "其他已选目录",
139
- "links.hint": "保存后按勾选建立或移除接入;技能本体保留在原位置,不会移动。",
140
- "autoLink.detected": "安装后自动接入",
141
- "autoLink.none": "本机没有检测到其他 Agent",
142
- "autoLink.hint": "列表里的「dsh(全局)」固定勾选、不能取消;这里只影响之后安装时的自动接入,已安装的技能请在「接入设置」里改。",
127
+ "mode.offHint": "已停用:DSH 的模型调用与 `/` 手动调用都看不到它;技能文件、导出关系与其他 Agent 都不受影响",
128
+ "visibility.hiddenHint": "已启用,但扫描目录里没有它:确认 ~/.agents/skills 下真有这个技能,或检查 DSH_AGENTS_HOME。",
129
+ "visibility.manualOnlyHint": "该技能自己声明了 `disable-model-invocation`:模型调用看不到它,手动 `/` 调用仍可用。",
130
+ "visibility.scopeMismatch": "当前有另一份同名技能排在前面(项目或用户目录);停用只影响 canonical,不会隐藏它。",
131
+ "visibility.foreignWinner": "模型看到的是另一份同名技能:{path}(它的扫描根 rank 更小,会遮住这一份)",
132
+ "exports.targets": "导出到哪些 Agent",
133
+ "exports.detectedHint": "只列出本机已安装(目录存在)的 Agent。导出只影响它们;DSH 自己看不看得见由 DSH 决定,和导出无关。",
134
+ "exports.canonical": "技能本体:{path};下面的链只是把它暴露给不直接读该目录的 Agent。",
135
+ "exports.none": "本机没有可导出的 Agent 目录",
136
+ "exports.hint": "保存后按勾选建立或移除软链;技能本体留在原位置,不会移动。",
137
+ "exports.sharedUnknown": "共享根未验证",
138
+ "exportDefault.detected": "装完后默认导出到",
139
+ "exportDefault.none": "本机没有需要导出的 Agent",
140
+ "exportDefault.hint": "默认一个都不勾:技能装进 ~/.agents/skills 后 DSH 本来就能看见。导出只用于仍只认私有目录的 Agent,且只影响之后的安装。",
143
141
  "empty.skills": "还没有安装任何技能",
144
142
  "empty.skillsHint": "在终端跑 `npx skills add owner/repo`,或点右上角「添加技能」",
145
143
  "empty.subs": "还没有添加订阅",
@@ -167,17 +165,19 @@ var DICT = {
167
165
  "form.subHint": "只记录你关注这个仓库,不会安装任何技能。",
168
166
  "subscription.defaultRef": "默认分支",
169
167
  "confirm.deleteTitle": "删除技能",
170
- "confirm.deleteBody": "会从本机移除,断开本插件建立的软链并调 skills CLI 卸载;目录先备份到回收站(保留 7 天)。不会删除你的源仓库。",
168
+ "confirm.deleteBody": "会从本机移除,断开本插件建立的软链并删除 canonical / lock 条目;目录先备份到回收站(保留 7 天)。不会删除你的源仓库。",
171
169
  "confirm.updateTitle": "本地有改动",
172
170
  "confirm.updateBody": "更新会覆盖这些本地改动;旧内容已备份,可回滚。",
173
171
  "error.conflictTitle": "删除被拦截",
174
172
  "busy.working": "处理中…",
175
173
  "meta.from": "来自",
176
174
  "meta.noUpstream": "无远端来源 · 不检测更新",
177
- "meta.unlinked": "未接入任何 Agent",
178
- "meta.scope": "范围",
179
- "meta.onlyProject": "仅",
180
- "meta.agentsAll": "已接入的 Agent",
175
+ "meta.noExport": "未导出",
176
+ "meta.noExportHint": "没有给不支持共享根的 Agent 建导出链;直读 ~/.agents/skills 的 Agent 装好即见,无需导出。",
177
+ "meta.modelVisible": "模型可见",
178
+ "meta.modelHidden": "模型不可见",
179
+ "meta.manualOnly": "仅手动",
180
+ "meta.agentsAll": "已导出的 Agent",
181
181
  "meta.none": "无",
182
182
  "filter.all": "全部",
183
183
  "filter.update": "可更新",
@@ -189,16 +189,15 @@ var DICT = {
189
189
  "empty.filtered": "没有匹配的技能",
190
190
  "empty.filteredHint": "换个关键词,或清除筛选条件",
191
191
  "hint.unknown": "还有 {n} 个技能未检查更新。",
192
- "group.dshGlobal": "DSH · 全局",
193
- "group.dshProject": "DSH · 仅特定项目",
192
+ "group.visible": "模型可见",
194
193
  "group.off": "已停用",
195
- "group.unlinked": "未接入 Agent",
194
+ "group.hidden": "启用但不可见",
196
195
  installed: "已安装",
197
196
  notInstalled: "未安装",
198
197
  skillCount: "个技能",
199
198
  visibleOnly: "模型可见,但不在本地技能库",
200
- visibleOnlyOther: "DSH 能看到,但不在 ~/.agents/skills",
201
- visibleOnlyBuiltin: "本插件仅管理 ~/.agents/skills 下的技能,DSH 内置技能不在管理范围内",
199
+ visibleOnlyOther: "模型能看到,但不在 ~/.agents/skills",
200
+ visibleOnlyBuiltin: "本插件仅管理 ~/.agents/skills 下的技能,内置技能不在管理范围内",
202
201
  "builtin.view": "查看内置技能({n})",
203
202
  "builtin.hide": "收起内置技能",
204
203
  external: "非本插件创建",
@@ -250,10 +249,8 @@ var DICT = {
250
249
  "viewer.info.localChangeNone": "无(与安装时一致)",
251
250
  "viewer.info.localChangeYes": "有本地改动",
252
251
  "viewer.info.links": "接入",
253
- "viewer.info.agents": "Agent",
254
- "viewer.info.scope": "范围",
255
- "viewer.info.scopeGlobal": "全局",
256
- "viewer.info.scopeProject": "仅项目",
252
+ "viewer.info.exported": "已导出",
253
+ "viewer.info.visibility": "可见性",
257
254
  "viewer.info.mode": "状态",
258
255
  "viewer.info.status": "更新",
259
256
  "viewer.info.none": "无"
@@ -273,18 +270,19 @@ var DICT = {
273
270
  "action.unsubscribe": "Unsubscribe",
274
271
  "action.view": "View",
275
272
  "action.hide": "Hide",
276
- "action.links": "Link settings",
273
+ "action.exports": "Export settings",
277
274
  "action.install": "Install",
278
275
  "action.expand": "Expand",
279
276
  "action.collapse": "Collapse",
280
277
  "action.adopt": "Adopt",
281
278
  "action.save": "Save",
282
279
  "action.cancel": "Cancel",
280
+ "action.close": "Close",
283
281
  "action.choose": "Choose folder",
284
282
  "action.retry": "Reload",
285
283
  "view.flat": "Flat",
286
284
  "view.agent": "By agent",
287
- "autoLink": "Auto-link",
285
+ "exportDefault": "Auto-export on install",
288
286
  "settings.title": "Plugin settings",
289
287
  "placement.label": "Sidebar entry",
290
288
  "placement.effect": "Applies immediately",
@@ -309,23 +307,20 @@ var DICT = {
309
307
  "toast.checked": "Checked",
310
308
  "mode.on": "Enabled",
311
309
  "mode.off": "Disabled",
312
- "mode.offHint": "Disabled: DSH cannot see it, other linked agents still can",
313
- "links.dshGlobal": "dsh (global)",
314
- "links.required": "required",
315
- "links.scope": "Scope inside DSH",
316
- "links.scopeGlobal": "Global (default)",
317
- "links.scopeProjects": "Only these projects",
318
- "links.targets": "Link to agents",
319
- "links.detectedHint": "Only installed agents are listed; deleted agents disappear but their old link records are kept and hidden.",
320
- "links.canonical": "Skill source: {path}; the entries below are each agent's link target.",
321
- "links.workspaces": "DSH workspaces",
322
- "links.noWorkspaces": 'No DSH workspaces yet; use "Choose another folder" below',
323
- "links.pickDirectory": "Choose another folder…",
324
- "links.selected": "Other selected folders",
325
- "links.hint": "Saving only adds or removes links; the skill itself stays where it is.",
326
- "autoLink.detected": "Auto-link after install",
327
- "autoLink.none": "No other agents detected on this machine",
328
- "autoLink.hint": 'The "dsh (global)" entry is always checked and cannot be unchecked; this only affects future installs. Change existing skills under "Link settings".',
310
+ "mode.offHint": "Disabled for DSH’s model and `/` calls; the skill files, exports, and other agents are untouched",
311
+ "visibility.hiddenHint": "Enabled, but no copy sits in a scan directory: check that the skill really exists under ~/.agents/skills, or inspect DSH_AGENTS_HOME.",
312
+ "visibility.manualOnlyHint": "This skill declares `disable-model-invocation`: the model cannot invoke it, but manual `/` invocation still works.",
313
+ "visibility.scopeMismatch": "Another copy of this skill ranks first (project or user directory); disabling only affects the canonical copy.",
314
+ "visibility.foreignWinner": "The model sees a different copy of this skill: {path} (its scan root ranks higher, so it shadows this one)",
315
+ "exports.targets": "Export to agents",
316
+ "exports.detectedHint": "Only installed agents (existing directories) are listed. Exports only affect them; whether DSH sees the skill is up to DSH, not exports.",
317
+ "exports.canonical": "Skill source: {path}; the entries below only expose it to agents that do not read that directory directly.",
318
+ "exports.none": "No agent directories available for export on this machine",
319
+ "exports.hint": "Saving only adds or removes links; the skill itself stays where it is.",
320
+ "exports.sharedUnknown": "shared root unverified",
321
+ "exportDefault.detected": "Export new installs to",
322
+ "exportDefault.none": "No agents need exporting on this machine",
323
+ "exportDefault.hint": "Nothing is checked by default: once a skill lands in ~/.agents/skills, DSH already sees it. Exports only matter for agents that still read their own directory, and only affect future installs.",
329
324
  "empty.skills": "No skills installed yet",
330
325
  "empty.skillsHint": 'Run `npx skills add owner/repo` or use "Add skill".',
331
326
  "empty.subs": "No subscriptions yet",
@@ -353,17 +348,19 @@ var DICT = {
353
348
  "form.subHint": "Only records the repository; installs nothing.",
354
349
  "subscription.defaultRef": "default branch",
355
350
  "confirm.deleteTitle": "Delete skill",
356
- "confirm.deleteBody": "Removes it locally, disconnects this plugin’s links and calls skills remove. A snapshot stays in trash for 7 days. Your source repo is untouched.",
351
+ "confirm.deleteBody": "Removes it locally, disconnects this plugin’s links, and deletes the canonical directory and lock entry. A snapshot stays in trash for 7 days. Your source repo is untouched.",
357
352
  "confirm.updateTitle": "Local changes",
358
353
  "confirm.updateBody": "Updating overwrites local changes; the old content is backed up and can be rolled back.",
359
354
  "error.conflictTitle": "Delete blocked",
360
355
  "busy.working": "Working…",
361
356
  "meta.from": "from",
362
357
  "meta.noUpstream": "no upstream · updates not checked",
363
- "meta.unlinked": "not linked to any agent",
364
- "meta.scope": "scope",
365
- "meta.onlyProject": "only",
366
- "meta.agentsAll": "Connected agents",
358
+ "meta.noExport": "Not exported",
359
+ "meta.noExportHint": "No export links to agents that need them; agents that read ~/.agents/skills can use it as-is.",
360
+ "meta.modelVisible": "visible to the model",
361
+ "meta.modelHidden": "hidden from the model",
362
+ "meta.manualOnly": "Manual only",
363
+ "meta.agentsAll": "Exported agents",
367
364
  "meta.none": "none",
368
365
  "filter.all": "All",
369
366
  "filter.update": "Updates",
@@ -375,16 +372,15 @@ var DICT = {
375
372
  "empty.filtered": "No matching skills",
376
373
  "empty.filteredHint": "Try another keyword, or clear the filters",
377
374
  "hint.unknown": "{n} skills have not been checked for updates.",
378
- "group.dshGlobal": "DSH · global",
379
- "group.dshProject": "DSH · project only",
375
+ "group.visible": "Model visible",
380
376
  "group.off": "Disabled",
381
- "group.unlinked": "Not linked",
377
+ "group.hidden": "Enabled but hidden",
382
378
  installed: "installed",
383
379
  notInstalled: "not installed",
384
380
  skillCount: "skills",
385
381
  visibleOnly: "Visible to the model but not in the local skill library",
386
- visibleOnlyOther: "DSH can see these, but they are not under ~/.agents/skills",
387
- visibleOnlyBuiltin: "This plugin only manages skills under ~/.agents/skills; DSH built-in skills are out of scope",
382
+ visibleOnlyOther: "The model can see these, but they are not under ~/.agents/skills",
383
+ visibleOnlyBuiltin: "This plugin only manages skills under ~/.agents/skills; built-in skills are out of scope",
388
384
  "builtin.view": "View built-ins ({n})",
389
385
  "builtin.hide": "Hide built-ins",
390
386
  external: "created outside this plugin",
@@ -437,10 +433,8 @@ var DICT = {
437
433
  "viewer.info.localChangeNone": "None (matches install)",
438
434
  "viewer.info.localChangeYes": "Modified locally",
439
435
  "viewer.info.links": "Linked",
440
- "viewer.info.agents": "Agents",
441
- "viewer.info.scope": "Scope",
442
- "viewer.info.scopeGlobal": "Global",
443
- "viewer.info.scopeProject": "Project only",
436
+ "viewer.info.exported": "Exported",
437
+ "viewer.info.visibility": "Visibility",
444
438
  "viewer.info.mode": "State",
445
439
  "viewer.info.status": "Updates",
446
440
  "viewer.info.none": "None"
@@ -1023,6 +1017,10 @@ var CSS = `
1023
1017
  .dshsc-row.off { opacity: .7; }
1024
1018
  .dshsc-cell { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
1025
1019
  .dshsc-namerow { display: flex; align-items: center; gap: 7px; min-width: 0; }
1020
+ /* 可见性状态点:中性表达(实心=模型可见,空心=模型不可见),不绑定任何 Agent 身份 */
1021
+ .dshsc-vdot { flex: none; width: 6px; height: 6px; border-radius: 50%; box-sizing: border-box;
1022
+ background: var(--dsw-alias-state-success-primary); opacity: .75; }
1023
+ .dshsc-vdot.hidden { background: transparent; border: 1px solid var(--dsw-alias-label-tertiary); opacity: .9; }
1026
1024
  .dshsc-name { font-size: 13px; font-weight: 600; letter-spacing: -.005em; min-width: 0;
1027
1025
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1028
1026
  /* 技能名本身是查看入口:清掉 button 默认外观,保留 hover 下划线提示可点 */
@@ -1048,9 +1046,6 @@ var CSS = `
1048
1046
  .dshsc-agent { display: inline-flex; align-items: center; gap: 5px; flex: none; height: 20px; padding: 0 7px;
1049
1047
  border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; color: var(--dsw-alias-label-secondary);
1050
1048
  background: color-mix(in srgb, var(--dsw-alias-bg-layer-2) 55%, transparent); font-size: 11px; white-space: nowrap; }
1051
- .dshsc-agent.dsh { color: var(--dsw-alias-brand-primary); border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 30%, var(--dsw-alias-border-l1));
1052
- background: color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent); }
1053
- .dshsc-agent .dshsc-dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; opacity: .8; }
1054
1049
  .dshsc-agent.more, .dshsc-agent.scope { border-style: dashed; background: transparent; color: var(--dsw-alias-label-tertiary); }
1055
1050
  .dshsc-agent.more { margin: 0; font: inherit; font-size: 11px; line-height: 1; cursor: pointer;
1056
1051
  -webkit-appearance: none; appearance: none; }
@@ -1467,7 +1462,7 @@ function useSkillActions({ t, rows, reload, run, setBusy, setToast, setModal })
1467
1462
  });
1468
1463
  }
1469
1464
  async function toggleMode(row) {
1470
- const mode = row.mode === "on" ? "off" : "on";
1465
+ const mode = row.enabled === false ? "on" : "off";
1471
1466
  await run(`mode:${row.name}`, () => apiCall("POST", `/skills/${encodeURIComponent(row.name)}/mode`, { mode }));
1472
1467
  }
1473
1468
  async function updateOne(row, overwrite) {
@@ -1530,17 +1525,20 @@ function useSkillActions({ t, rows, reload, run, setBusy, setToast, setModal })
1530
1525
  }
1531
1526
  });
1532
1527
  }
1533
- async function saveLinks(row, targets, scopeProjects) {
1534
- const current = new Set(row.links.map((link) => link.target));
1535
- const desired = new Set(scopeProjects.length > 0 ? targets.filter((target) => target !== "dsh-global") : targets);
1528
+ async function saveExports(row, targets) {
1529
+ const current = new Set(row.exports.map((item) => item.target));
1530
+ const desired = new Set(targets);
1536
1531
  const toRemove = [...current].filter((target) => !desired.has(target));
1537
- await run(`links:${row.name}`, async () => {
1532
+ const toAdd = [...desired].filter((target) => !current.has(target));
1533
+ await run(`exports:${row.name}`, async () => {
1538
1534
  if (toRemove.length > 0) {
1539
- await apiCall("POST", "/links", { skill: row.name, targets: toRemove, action: "remove" });
1535
+ await apiCall("POST", "/exports", { skill: row.name, targets: toRemove, action: "remove" });
1536
+ }
1537
+ if (toAdd.length > 0) {
1538
+ await apiCall("POST", "/exports", { skill: row.name, targets: toAdd, action: "add" });
1540
1539
  }
1541
- await apiCall("POST", "/links", { skill: row.name, targets: [...desired], action: "add", scopeProjects });
1542
1540
  });
1543
- setToast({ kind: "ok", text: `${row.name} · ${t("action.links")} ✓` });
1541
+ setToast({ kind: "ok", text: `${row.name} · ${t("action.exports")} ✓` });
1544
1542
  }
1545
1543
  function requestAddSkill() {
1546
1544
  setModal({ type: "add-skill" });
@@ -1598,7 +1596,7 @@ function useSkillActions({ t, rows, reload, run, setBusy, setToast, setModal })
1598
1596
  updateAll,
1599
1597
  checkUpdates,
1600
1598
  requestDelete,
1601
- saveLinks,
1599
+ saveExports,
1602
1600
  requestAddSkill,
1603
1601
  requestAddSubscription,
1604
1602
  probeSkills,
@@ -1945,10 +1943,10 @@ function Header({
1945
1943
  h("button", {
1946
1944
  className: "dshsc-btn",
1947
1945
  disabled: !writable,
1948
- title: `${t("autoLink")}:${autoLinkLabel}`,
1949
- "aria-label": `${t("autoLink")}:${autoLinkLabel}`,
1946
+ title: `${t("exportDefault")}:${autoLinkLabel}`,
1947
+ "aria-label": `${t("exportDefault")}:${autoLinkLabel}`,
1950
1948
  onClick: onOpenAutoLink
1951
- }, `${t("autoLink")} · ${autoLinkCount} ▾`),
1949
+ }, `${t("exportDefault")} · ${autoLinkCount} ▾`),
1952
1950
  h("button", {
1953
1951
  className: "dshsc-btn primary",
1954
1952
  disabled: !writable,
@@ -2038,6 +2036,7 @@ function InstalledTab({
2038
2036
  actions
2039
2037
  }) {
2040
2038
  const updatable = (row) => UPDATABLE.has(row.status);
2039
+ const sharedTargets = new Set((data?.agents?.detected || []).filter((agent) => agent.kind === "agent" && agent.sharedHome === "yes").map((agent) => agent.target));
2041
2040
  function statusTag(row) {
2042
2041
  if (row.status === "unknown") return null;
2043
2042
  if (row.status === "no-upstream") {
@@ -2069,21 +2068,25 @@ function InstalledTab({
2069
2068
  );
2070
2069
  }
2071
2070
  function agentChips(row) {
2072
- const links = row.links.filter((link) => !link.broken);
2073
- const projectLinks = links.filter((link) => String(link.target).startsWith("project:"));
2074
- const labels = links.filter((link) => !String(link.target).startsWith("project:")).map((link) => link.target === "dsh-global" ? "DSH" : link.label);
2075
- if (projectLinks.length > 0 && !labels.includes("DSH")) labels.unshift("DSH");
2076
- const projects = row.scopeProjects || [];
2077
- const scopeNames = projects.length > 0 ? projects.map((project) => project.split("/").pop()) : projectLinks.map((link) => link.label);
2078
- if (labels.length === 0 && scopeNames.length === 0) {
2079
- return h("span", { className: "dshsc-unlinked" }, t("meta.unlinked"));
2071
+ const exported = (row.exports || []).filter(
2072
+ (item) => !item.broken && !sharedTargets.has(item.target)
2073
+ );
2074
+ if (exported.length === 0) {
2075
+ return [h("span", {
2076
+ key: "none",
2077
+ className: "dshsc-unlinked",
2078
+ title: t("meta.noExportHint")
2079
+ }, t("meta.noExport"))];
2080
2080
  }
2081
- const chips = labels.slice(0, 3).map((label, index) => h("span", {
2082
- className: `dshsc-agent${label === "DSH" ? " dsh" : ""}`,
2081
+ const chips = [];
2082
+ const labels = exported.map((item) => item.label);
2083
+ const shown = exported.slice(0, 3);
2084
+ shown.forEach((item, index) => chips.push(h("span", {
2085
+ className: "dshsc-agent",
2083
2086
  key: `agent-${index}`,
2084
- title: label
2085
- }, label === "DSH" ? h("i", { className: "dshsc-dot" }) : null, label));
2086
- if (labels.length > 3) {
2087
+ title: item.label
2088
+ }, item.label)));
2089
+ if (exported.length > shown.length) {
2087
2090
  const open = agentTip?.rowName === row.name;
2088
2091
  chips.push(h("button", {
2089
2092
  type: "button",
@@ -2097,21 +2100,17 @@ function InstalledTab({
2097
2100
  onFocus: (event) => actions.onOpenAgentTip(event, row.name, labels),
2098
2101
  onMouseLeave: actions.onCloseAgentTip,
2099
2102
  onBlur: actions.onCloseAgentTip
2100
- }, `+${labels.length - 3}`));
2101
- }
2102
- if (scopeNames.length > 0) {
2103
- chips.push(h("span", {
2104
- className: "dshsc-agent scope",
2105
- key: "scope",
2106
- title: `${t("meta.scope")}:${projects.length > 0 ? projects.join("、") : scopeNames.join("、")}`
2107
- }, `${t("meta.onlyProject")} ${scopeNames.join("、")}`));
2103
+ }, `+${exported.length - shown.length}`));
2108
2104
  }
2109
2105
  return chips;
2110
2106
  }
2111
2107
  function rowView(row) {
2112
- const off = row.mode === "off";
2108
+ const off = row.enabled === false;
2113
2109
  const tracked = row.status !== "unmanaged";
2114
2110
  const canUpdate = writable && updatable(row);
2111
+ const hidden = !off && !row.visible;
2112
+ const manualOnly = hidden && Boolean(row.visibility?.selfHidden);
2113
+ const visLabel = hidden ? t("meta.modelHidden") : t("meta.modelVisible");
2115
2114
  return h(
2116
2115
  "div",
2117
2116
  { className: `dshsc-row${off ? " off" : ""}`, key: row.name },
@@ -2121,6 +2120,12 @@ function InstalledTab({
2121
2120
  h(
2122
2121
  "div",
2123
2122
  { className: "dshsc-namerow" },
2123
+ off ? null : h("span", {
2124
+ className: `dshsc-vdot${hidden ? " hidden" : ""}`,
2125
+ role: "img",
2126
+ title: visLabel,
2127
+ "aria-label": visLabel
2128
+ }),
2124
2129
  h("button", {
2125
2130
  type: "button",
2126
2131
  className: "dshsc-name dshsc-namebtn",
@@ -2129,10 +2134,25 @@ function InstalledTab({
2129
2134
  onClick: () => actions.onView(row)
2130
2135
  }, row.name),
2131
2136
  statusTag(row),
2137
+ manualOnly ? h("span", {
2138
+ className: "dshsc-tag off",
2139
+ title: t("visibility.manualOnlyHint"),
2140
+ "aria-label": t("visibility.manualOnlyHint")
2141
+ }, t("meta.manualOnly")) : null,
2132
2142
  off ? h("span", { className: "dshsc-tag off" }, t("status.off")) : null
2133
2143
  ),
2134
2144
  sourceLine(row),
2135
2145
  off ? h("div", { className: "dshsc-note" }, t("mode.offHint")) : null,
2146
+ // manual-only 用名字旁的「仅手动」标志表达,不占一行警告;
2147
+ // 被更靠前的同名副本盖住时由下面的 foreignWinner 点名,这里不再补「扫描目录里没有它」。
2148
+ hidden && !manualOnly && row.visibility?.winnerIsCanonical !== false ? h("div", { className: "dshsc-note warn" }, t("visibility.hiddenHint")) : null,
2149
+ row.visibility?.shadowScopeMismatch ? h("div", { className: "dshsc-note warn" }, t("visibility.scopeMismatch")) : null,
2150
+ // 同名副本在更小的 rank 上:模型看到的不是这一份,必须点名路径
2151
+ row.visibility?.winnerIsCanonical === false ? h(
2152
+ "div",
2153
+ { className: "dshsc-note warn" },
2154
+ t("visibility.foreignWinner").replace("{path}", row.visibility.winnerPath || "?")
2155
+ ) : null,
2136
2156
  row.status === "broken-link" ? h("div", { className: "dshsc-note warn" }, t("status.brokenHint")) : null
2137
2157
  ),
2138
2158
  h("div", { className: "dshsc-agents" }, agentChips(row)),
@@ -2142,12 +2162,12 @@ function InstalledTab({
2142
2162
  h("button", {
2143
2163
  className: "dshsc-switch",
2144
2164
  role: "switch",
2145
- "aria-checked": String(tracked && row.mode === "on"),
2165
+ "aria-checked": String(tracked && row.enabled !== false),
2146
2166
  "aria-label": `${row.name} ${t("mode.on")}/${t("mode.off")}`,
2147
2167
  // 未纳入追踪时调 setMode 只会报「没有安装记录」,不如直接禁用
2148
2168
  disabled: busy !== null || !tracked,
2149
2169
  onClick: () => void actions.onToggleMode(row),
2150
- title: tracked ? row.mode === "on" ? t("mode.off") : t("mode.on") : t("adopt.switchHint")
2170
+ title: tracked ? row.enabled !== false ? t("mode.off") : t("mode.on") : t("adopt.switchHint")
2151
2171
  }, h("span", { className: "dshsc-knob" })),
2152
2172
  h("button", {
2153
2173
  className: "dshsc-btn ghost dshsc-view",
@@ -2166,7 +2186,7 @@ function InstalledTab({
2166
2186
  disabled: !canUpdate || busy !== null,
2167
2187
  onClick: () => actions.onUpdate(row)
2168
2188
  }, t("action.update")),
2169
- h("button", { className: "dshsc-btn ghost", disabled: busy !== null, onClick: () => actions.onLinks(row) }, t("action.links")),
2189
+ h("button", { className: "dshsc-btn ghost", disabled: busy !== null, onClick: () => actions.onExports(row) }, t("action.exports")),
2170
2190
  h("button", {
2171
2191
  className: "dshsc-btn ghost danger sq",
2172
2192
  disabled: !writable || busy !== null,
@@ -2179,18 +2199,17 @@ function InstalledTab({
2179
2199
  }
2180
2200
  function groupedRows() {
2181
2201
  const groups = [];
2182
- const hasLink = (row, prefix) => row.links.some((link) => !link.broken && String(link.target).startsWith(prefix));
2183
- const dshRows = visibleRows.filter((row) => row.mode === "on" && !row.scopeProjects?.length && hasLink(row, "dsh-global"));
2184
- const projectRows = visibleRows.filter((row) => row.mode === "on" && row.scopeProjects?.length);
2185
- const offRows = visibleRows.filter((row) => row.mode === "off");
2186
- const unlinkedRows = visibleRows.filter((row) => row.mode === "on" && !row.scopeProjects?.length && !row.links.some((link) => !link.broken));
2187
- groups.push({ key: "dsh-global", title: t("group.dshGlobal"), rows: dshRows, dot: true });
2188
- if (projectRows.length) groups.push({ key: "dsh-project", title: t("group.dshProject"), rows: projectRows, dot: true });
2202
+ const visibleGroup = visibleRows.filter((row) => row.enabled !== false && row.visible);
2203
+ const offRows = visibleRows.filter((row) => row.enabled === false);
2204
+ const hiddenRows = visibleRows.filter((row) => row.enabled !== false && !row.visible);
2205
+ groups.push({ key: "visible", title: t("group.visible"), rows: visibleGroup, dot: true });
2189
2206
  if (offRows.length) groups.push({ key: "off", title: t("group.off"), rows: offRows });
2190
- if (unlinkedRows.length) groups.push({ key: "unlinked", title: t("group.unlinked"), rows: unlinkedRows });
2207
+ if (hiddenRows.length) groups.push({ key: "hidden", title: t("group.hidden"), rows: hiddenRows });
2191
2208
  for (const agent of data?.agents?.detected || []) {
2192
2209
  if (agent.kind !== "agent") continue;
2193
- const matched = visibleRows.filter((row) => row.links.some((link) => link.target === agent.target));
2210
+ const matched = visibleRows.filter(
2211
+ (row) => (row.exports || []).some((item) => item.target === agent.target && !item.broken)
2212
+ );
2194
2213
  if (matched.length) groups.push({ key: agent.target, title: agent.label, rows: matched });
2195
2214
  }
2196
2215
  return groups.filter((group) => group.rows.length > 0);
@@ -2345,6 +2364,7 @@ function InstalledTab({
2345
2364
 
2346
2365
  // client/views/agent-tip.js
2347
2366
  function AgentTip({ t, agentTip, agentTipPos, nodeRef }) {
2367
+ const labels = agentTip.labels || [];
2348
2368
  const node = h(
2349
2369
  "div",
2350
2370
  {
@@ -2358,15 +2378,16 @@ function AgentTip({ t, agentTip, agentTipPos, nodeRef }) {
2358
2378
  "div",
2359
2379
  { className: "dshsc-agenttip-head" },
2360
2380
  h("span", null, t("meta.agentsAll")),
2361
- h("span", { className: "dshsc-agenttip-n" }, agentTip.labels.length)
2381
+ h("span", { className: "dshsc-agenttip-n" }, labels.length)
2362
2382
  ),
2363
2383
  h(
2364
2384
  "div",
2365
2385
  { className: "dshsc-agenttip-list" },
2366
- agentTip.labels.map((label, index) => h("span", {
2367
- className: `dshsc-agent${label === "DSH" ? " dsh" : ""}`,
2368
- key: `agenttip-${index}`
2369
- }, label === "DSH" ? h("i", { className: "dshsc-dot" }) : null, label))
2386
+ labels.map((label, index) => h("span", {
2387
+ className: "dshsc-agent",
2388
+ key: `agenttip-${index}`,
2389
+ title: label
2390
+ }, label))
2370
2391
  )
2371
2392
  );
2372
2393
  return portalize(node);
@@ -2687,124 +2708,65 @@ function FormModal({ t, title, hint, fields, onCancel, onSubmit }) {
2687
2708
  ))
2688
2709
  );
2689
2710
  }
2690
- function ConfirmModal({ t, title, body, onCancel, onConfirm, danger, confirmLabel }) {
2711
+ function ConfirmModal({ t, title, body, onCancel, onConfirm, danger, confirmLabel, hideCancel = false }) {
2691
2712
  return h(Overlay, {
2692
2713
  t,
2693
2714
  title,
2694
2715
  onCancel,
2695
2716
  footer: [
2696
- h("button", { key: "c", className: "dshsc-btn ghost", onClick: onCancel }, t("action.cancel")),
2717
+ ...hideCancel ? [] : [h("button", { key: "c", className: "dshsc-btn ghost", onClick: onCancel }, t("action.cancel"))],
2697
2718
  h("button", { key: "o", className: `dshsc-btn${danger ? " danger" : " primary"}`, onClick: onConfirm }, confirmLabel || t("action.save"))
2698
2719
  ]
2699
2720
  }, h("p", { className: "dshsc-sub", style: { whiteSpace: "pre-wrap" } }, body));
2700
2721
  }
2701
- function LinksModal({ t, row, agents, workspaces, pickDirectory, onCancel, onSave }) {
2722
+ function ExportsModal({ t, row, agents, onCancel, onSave }) {
2702
2723
  const candidates = agents.filter(
2703
- (agent) => agent.kind === "dsh" || agent.kind !== "project" && agent.exists
2724
+ (agent) => agent.kind === "agent" && agent.exists && agent.exportable
2704
2725
  );
2705
2726
  const [targets, setTargets] = import_react4.default.useState(() => {
2706
- const visible = new Set(candidates.map((agent) => agent.target));
2707
- const initial = new Set(row.links.map((link) => link.target).filter((target) => visible.has(target)));
2708
- initial.add("dsh-global");
2709
- return initial;
2727
+ const available = new Set(candidates.map((agent) => agent.target));
2728
+ return new Set((row.exports || []).map((item) => item.target).filter((target) => available.has(target)));
2710
2729
  });
2711
- const [scopeProjects, setScopeProjects] = import_react4.default.useState(row.scopeProjects || []);
2712
- const [scopeMode, setScopeMode] = import_react4.default.useState((row.scopeProjects || []).length > 0 ? "projects" : "global");
2713
- const workspacePaths = new Set(workspaces.map((workspace) => workspace.path));
2714
- const otherProjects = scopeProjects.filter((project) => !workspacePaths.has(project));
2715
2730
  const toggle = (target) => setTargets((prev) => {
2716
2731
  const next = new Set(prev);
2717
2732
  if (next.has(target)) next.delete(target);
2718
2733
  else next.add(target);
2719
2734
  return next;
2720
2735
  });
2721
- const toggleProject = (project) => setScopeProjects((prev) => prev.includes(project) ? prev.filter((item) => item !== project) : [...prev, project]);
2722
- const chooseDirectory = async () => {
2723
- const dir = await pickDirectory?.();
2724
- if (!dir) return;
2725
- setScopeProjects((prev) => prev.includes(dir) ? prev : [...prev, dir]);
2726
- };
2727
2736
  return h(
2728
2737
  Overlay,
2729
2738
  {
2730
2739
  t,
2731
- title: `${t("action.links")} · ${row.name}`,
2740
+ title: `${t("action.exports")} · ${row.name}`,
2732
2741
  onCancel,
2733
2742
  footer: [
2734
2743
  h("button", { key: "c", className: "dshsc-btn ghost", onClick: onCancel }, t("action.cancel")),
2735
- h("button", {
2736
- key: "s",
2737
- className: "dshsc-btn primary",
2738
- onClick: () => onSave([...targets], scopeMode === "projects" ? scopeProjects.filter((item) => item.trim()) : [])
2739
- }, t("action.save"))
2744
+ h("button", { key: "s", className: "dshsc-btn primary", onClick: () => onSave([...targets]) }, t("action.save"))
2740
2745
  ]
2741
2746
  },
2742
- h("div", { className: "dshsc-group" }, t("links.targets")),
2743
- h("p", { className: "dshsc-sub" }, t("links.detectedHint")),
2744
- h("p", { className: "dshsc-sub" }, t("links.canonical").replace("{path}", `~/.agents/skills/${row.name}`)),
2747
+ h("div", { className: "dshsc-group" }, t("exports.targets")),
2748
+ h("p", { className: "dshsc-sub" }, t("exports.detectedHint")),
2749
+ h("p", { className: "dshsc-sub" }, t("exports.canonical").replace("{path}", `~/.agents/skills/${row.name}`)),
2750
+ candidates.length === 0 ? h("p", { className: "dshsc-sub" }, t("exports.none")) : null,
2745
2751
  ...candidates.map((agent) => h(
2746
2752
  "label",
2747
- { className: `dshsc-check${agent.target === "dsh-global" ? " locked" : ""}`, key: agent.target },
2753
+ { className: "dshsc-check", key: agent.target },
2748
2754
  h("input", {
2749
2755
  type: "checkbox",
2750
- checked: agent.target === "dsh-global" ? true : targets.has(agent.target),
2751
- disabled: agent.target === "dsh-global",
2756
+ checked: targets.has(agent.target),
2752
2757
  onChange: () => toggle(agent.target)
2753
2758
  }),
2754
- h("span", null, agent.kind === "dsh" ? t("links.dshGlobal") : agent.label),
2755
- agent.target === "dsh-global" ? h("small", null, t("links.required")) : null,
2759
+ h("span", null, agent.label),
2760
+ // 候选里已经没有「读共享根」的 agent 了;'no' 是常态不必标注,只提示没验证过的
2761
+ agent.sharedHome === "unknown" ? h("small", null, t("exports.sharedUnknown")) : null,
2756
2762
  h("small", { title: agent.dir }, agent.dir)
2757
2763
  )),
2758
- h("div", { className: "dshsc-group" }, t("links.scope")),
2759
- h(
2760
- "label",
2761
- { className: "dshsc-check" },
2762
- h("input", { type: "radio", name: `scope-${row.name}`, checked: scopeMode === "global", onChange: () => setScopeMode("global") }),
2763
- h("span", null, t("links.scopeGlobal"))
2764
- ),
2765
- h(
2766
- "label",
2767
- { className: "dshsc-check" },
2768
- h("input", { type: "radio", name: `scope-${row.name}`, checked: scopeMode === "projects", onChange: () => setScopeMode("projects") }),
2769
- h("span", null, t("links.scopeProjects"))
2770
- ),
2771
- scopeMode === "projects" ? h(
2772
- "div",
2773
- null,
2774
- h("div", { className: "dshsc-group" }, t("links.workspaces")),
2775
- workspaces.length === 0 ? h("p", { className: "dshsc-sub" }, t("links.noWorkspaces")) : h("div", { className: "dshsc-panel" }, workspaces.map((workspace) => h(
2776
- "label",
2777
- { className: "dshsc-check", key: workspace.id },
2778
- h("input", {
2779
- type: "checkbox",
2780
- checked: scopeProjects.includes(workspace.path),
2781
- onChange: () => toggleProject(workspace.path)
2782
- }),
2783
- h("span", null, workspace.title),
2784
- h("small", null, workspace.path)
2785
- ))),
2786
- otherProjects.length > 0 ? h("div", { className: "dshsc-group" }, t("links.selected")) : null,
2787
- ...otherProjects.map((project) => h(
2788
- "div",
2789
- { className: "dshsc-sub-entry", key: project },
2790
- h("span", { className: "dshsc-code" }, project),
2791
- h(
2792
- "div",
2793
- { className: "dshsc-actions" },
2794
- h("button", {
2795
- className: "dshsc-btn ghost danger",
2796
- onClick: () => setScopeProjects((prev) => prev.filter((item) => item !== project))
2797
- }, t("action.delete"))
2798
- )
2799
- )),
2800
- h("button", { className: "dshsc-btn", style: { alignSelf: "flex-start", marginTop: 6 }, onClick: () => void chooseDirectory() }, t("links.pickDirectory"))
2801
- ) : null,
2802
- h("p", { className: "dshsc-sub" }, t("links.hint"))
2764
+ h("p", { className: "dshsc-sub" }, t("exports.hint"))
2803
2765
  );
2804
2766
  }
2805
- function AutoLinkModal({ t, settings, agents, dsh, onCancel, onSave }) {
2767
+ function DefaultExportModal({ t, settings, agents, onCancel, onSave }) {
2806
2768
  const [targets, setTargets] = import_react4.default.useState(() => new Set(
2807
- Array.isArray(settings.autoLinkTargets) ? settings.autoLinkTargets : agents.map((agent) => agent.target)
2769
+ Array.isArray(settings.exportTargets) ? settings.exportTargets : []
2808
2770
  ));
2809
2771
  const toggle = (target) => setTargets((prev) => {
2810
2772
  const next = new Set(prev);
@@ -2816,50 +2778,29 @@ function AutoLinkModal({ t, settings, agents, dsh, onCancel, onSave }) {
2816
2778
  Overlay,
2817
2779
  {
2818
2780
  t,
2819
- title: t("autoLink"),
2781
+ title: t("exportDefault"),
2820
2782
  onCancel,
2821
2783
  footer: [
2822
2784
  h("button", { key: "c", className: "dshsc-btn ghost", onClick: onCancel }, t("action.cancel")),
2823
2785
  h("button", { key: "s", className: "dshsc-btn primary", onClick: () => onSave([...targets]) }, t("action.save"))
2824
2786
  ]
2825
2787
  },
2826
- h("div", { className: "dshsc-group" }, t("autoLink.detected")),
2827
- h(
2828
- "div",
2829
- { className: "dshsc-panel" },
2830
- // dsh(全局)始终接入:列表里也勾上,只是不能改,勾选数与页头的计数器才对得上
2831
- h(
2832
- "label",
2833
- { className: "dshsc-check locked", key: "dsh-global" },
2834
- h("input", {
2835
- type: "checkbox",
2836
- checked: true,
2837
- disabled: true,
2838
- readOnly: true,
2839
- onChange: () => {
2840
- }
2841
- }),
2842
- h("span", null, dsh?.label || t("links.dshGlobal")),
2843
- h("small", null, t("links.required")),
2844
- dsh?.dir ? h("small", { title: dsh.dir }, dsh.dir) : null
2845
- ),
2846
- ...agents.map((agent) => h(
2847
- "label",
2848
- { className: "dshsc-check", key: agent.target },
2849
- h("input", {
2850
- type: "checkbox",
2851
- checked: targets.has(agent.target),
2852
- onChange: () => toggle(agent.target)
2853
- }),
2854
- h("span", null, agent.label),
2855
- h("small", null, agent.dir)
2856
- ))
2857
- ),
2858
- agents.length === 0 ? h("p", { className: "dshsc-sub" }, t("autoLink.none")) : null,
2859
- h("p", { className: "dshsc-sub" }, t("autoLink.hint"))
2788
+ h("div", { className: "dshsc-group" }, t("exportDefault.detected")),
2789
+ agents.length === 0 ? h("p", { className: "dshsc-sub" }, t("exportDefault.none")) : h("div", { className: "dshsc-panel" }, agents.map((agent) => h(
2790
+ "label",
2791
+ { className: "dshsc-check", key: agent.target },
2792
+ h("input", {
2793
+ type: "checkbox",
2794
+ checked: targets.has(agent.target),
2795
+ onChange: () => toggle(agent.target)
2796
+ }),
2797
+ h("span", null, agent.label),
2798
+ h("small", null, agent.dir)
2799
+ ))),
2800
+ h("p", { className: "dshsc-sub" }, t("exportDefault.hint"))
2860
2801
  );
2861
2802
  }
2862
- function Modals({ t, modal, data, autoLinkAgents, autoLinkDsh, pickDirectory, actions, onClose, onSaveAutoLink }) {
2803
+ function Modals({ t, modal, data, autoLinkAgents, actions, onClose, onSaveAutoLink }) {
2863
2804
  if (modal.type === "add-skill") {
2864
2805
  return h(AddSkillModal, {
2865
2806
  t,
@@ -2882,26 +2823,23 @@ function Modals({ t, modal, data, autoLinkAgents, autoLinkDsh, pickDirectory, ac
2882
2823
  onSubmit: (form) => void actions.addSubscription(form)
2883
2824
  });
2884
2825
  }
2885
- if (modal.type === "links") {
2886
- return h(LinksModal, {
2826
+ if (modal.type === "exports") {
2827
+ return h(ExportsModal, {
2887
2828
  t,
2888
2829
  row: modal.row,
2889
2830
  agents: data?.agents?.all || [],
2890
- workspaces: data?.workspaces || [],
2891
- pickDirectory,
2892
2831
  onCancel: onClose,
2893
- onSave: (targets, scopeProjects) => {
2832
+ onSave: (targets) => {
2894
2833
  onClose();
2895
- void actions.saveLinks(modal.row, targets, scopeProjects);
2834
+ void actions.saveExports(modal.row, targets);
2896
2835
  }
2897
2836
  });
2898
2837
  }
2899
2838
  if (modal.type === "autolink") {
2900
- return h(AutoLinkModal, {
2839
+ return h(DefaultExportModal, {
2901
2840
  t,
2902
2841
  settings: data?.settings || {},
2903
2842
  agents: autoLinkAgents,
2904
- dsh: autoLinkDsh,
2905
2843
  onCancel: onClose,
2906
2844
  onSave: (targets) => void onSaveAutoLink(targets)
2907
2845
  });
@@ -2912,6 +2850,7 @@ function Modals({ t, modal, data, autoLinkAgents, autoLinkDsh, pickDirectory, ac
2912
2850
  title: t("confirm.deleteTitle"),
2913
2851
  body: t("confirm.deleteBody"),
2914
2852
  danger: true,
2853
+ confirmLabel: t("action.delete"),
2915
2854
  onCancel: onClose,
2916
2855
  onConfirm: modal.onConfirm
2917
2856
  });
@@ -2921,6 +2860,7 @@ function Modals({ t, modal, data, autoLinkAgents, autoLinkDsh, pickDirectory, ac
2921
2860
  t,
2922
2861
  title: t("confirm.updateTitle"),
2923
2862
  body: t("confirm.updateBody"),
2863
+ confirmLabel: t("action.update"),
2924
2864
  onCancel: onClose,
2925
2865
  onConfirm: modal.onConfirm
2926
2866
  });
@@ -2934,7 +2874,9 @@ function Modals({ t, modal, data, autoLinkAgents, autoLinkDsh, pickDirectory, ac
2934
2874
  ${conflicts.map((item) => `${item.label}: ${item.linkPath}`).join("\n")}`,
2935
2875
  onCancel: onClose,
2936
2876
  onConfirm: onClose,
2937
- confirmLabel: t("action.cancel")
2877
+ confirmLabel: t("action.close"),
2878
+ danger: true,
2879
+ hideCancel: true
2938
2880
  });
2939
2881
  }
2940
2882
  return null;
@@ -3433,7 +3375,7 @@ function SkillReader({
3433
3375
  onClose,
3434
3376
  onOpenPage,
3435
3377
  onUpdate,
3436
- onLinks,
3378
+ onExports,
3437
3379
  onToast
3438
3380
  }) {
3439
3381
  const { state, file, loadFile, retry } = useSkillReader(row);
@@ -3591,7 +3533,7 @@ function SkillReader({
3591
3533
  { className: "dshsc-reader-title" },
3592
3534
  h("h2", { id: "dshsc-reader-name", tabIndex: -1 }, name || t("viewer.title")),
3593
3535
  row?.status && STATUS_KEY[row.status] ? h("span", { className: `dshsc-tag ${row.status === "current" ? "ok" : "warn"}` }, t(STATUS_KEY[row.status])) : null,
3594
- row?.mode === "off" ? h("span", { className: "dshsc-tag off" }, t("status.off")) : null,
3536
+ row?.enabled === false ? h("span", { className: "dshsc-tag off" }, t("status.off")) : null,
3595
3537
  variant === "drawer" ? h("button", {
3596
3538
  type: "button",
3597
3539
  className: "dshsc-reader-close",
@@ -3830,8 +3772,7 @@ function SkillReader({
3830
3772
  );
3831
3773
  }
3832
3774
  function infoPanel() {
3833
- const agentLabels = (row?.links || []).filter((link) => !link.broken).map((link) => link.target === "dsh-global" ? "DSH" : link.label);
3834
- const projects = row?.scopeProjects || [];
3775
+ const exportLabels = (row?.exports || []).filter((item) => !item.broken).map((item) => item.label);
3835
3776
  const hash = row?.lockHash || row?.currentHash || null;
3836
3777
  const rows = [];
3837
3778
  rows.push(["viewer.info.name", h("span", { className: "mono" }, name), "mono"]);
@@ -3850,9 +3791,9 @@ function SkillReader({
3850
3791
  ["viewer.info.localChange", row?.status === "locally-modified" || row?.status === "conflict" ? t("viewer.info.localChangeYes") : t("viewer.info.localChangeNone")]
3851
3792
  ];
3852
3793
  const linkRows = [
3853
- ["viewer.info.agents", agentLabels.length > 0 ? agentLabels.join("、") : t("meta.unlinked")],
3854
- ["viewer.info.scope", projects.length > 0 ? `${t("viewer.info.scopeProject")}:${projects.map((item) => item.split("/").pop()).join("、")}` : t("viewer.info.scopeGlobal")],
3855
- ["viewer.info.mode", row?.mode === "off" ? t("status.off") : t("mode.on")],
3794
+ ["viewer.info.visibility", row?.visible ? t("meta.modelVisible") : t("meta.modelHidden")],
3795
+ ["viewer.info.exported", exportLabels.length > 0 ? exportLabels.join("、") : t("viewer.info.none")],
3796
+ ["viewer.info.mode", row?.enabled === false ? t("status.off") : t("mode.on")],
3856
3797
  ["viewer.info.status", STATUS_KEY[row?.status] ? t(STATUS_KEY[row.status]) : t("status.unknown")]
3857
3798
  ];
3858
3799
  return h(
@@ -3899,7 +3840,7 @@ function SkillReader({
3899
3840
  onClick: () => void copyTextWithToast(row?.path || "", "viewer.copied")
3900
3841
  }, t("viewer.copyPath")),
3901
3842
  variant === "drawer" && onOpenPage ? h("button", { type: "button", className: "dshsc-btn ghost", onClick: () => onOpenPage() }, t("viewer.openPage")) : null,
3902
- onLinks ? h("button", { type: "button", className: "dshsc-btn", onClick: () => onLinks?.(row) }, t("action.links")) : null,
3843
+ onExports ? h("button", { type: "button", className: "dshsc-btn", onClick: () => onExports?.(row) }, t("action.exports")) : null,
3903
3844
  onUpdate ? h("button", {
3904
3845
  type: "button",
3905
3846
  className: "dshsc-btn tint",
@@ -3991,15 +3932,15 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
3991
3932
  const updateCount = rows.filter(updatable).length;
3992
3933
  const bulkCount = rows.filter((row) => row.status === "update-available").length;
3993
3934
  const unknownCount = rows.filter((row) => row.status === "unknown").length;
3994
- const offCount = rows.filter((row) => row.mode === "off").length;
3935
+ const offCount = rows.filter((row) => row.enabled === false).length;
3995
3936
  const counts = { all: rows.length, update: updateCount, unknown: unknownCount, off: offCount };
3996
3937
  const queryText = query.trim().toLowerCase();
3997
3938
  const visibleRows = rows.filter((row) => {
3998
3939
  if (filter === "update" && !updatable(row)) return false;
3999
3940
  if (filter === "unknown" && row.status !== "unknown") return false;
4000
- if (filter === "off" && row.mode !== "off") return false;
3941
+ if (filter === "off" && row.enabled !== false) return false;
4001
3942
  if (!queryText) return true;
4002
- const agentLabels = row.links.map((link) => link.target === "dsh-global" ? "DSH" : link.label);
3943
+ const agentLabels = row.exports.map((item) => item.label);
4003
3944
  return [row.name, row.source || "", ...agentLabels].join(" ").toLowerCase().includes(queryText);
4004
3945
  });
4005
3946
  import_react6.default.useEffect(() => {
@@ -4008,11 +3949,10 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
4008
3949
  setViewer(null);
4009
3950
  setToast({ kind: "err", text: t("viewer.gone") });
4010
3951
  }, [data, rows, t, viewer]);
4011
- const autolinkTargets = (data?.agents?.detected || []).filter((agent) => agent.kind === "agent");
4012
- const autoLinkSelection = data?.settings?.autoLinkTargets;
4013
- const autoLinkAgents = Array.isArray(autoLinkSelection) ? autolinkTargets.filter((agent) => autoLinkSelection.includes(agent.target)) : autolinkTargets;
4014
- const autoLinkDsh = (data?.agents?.all || []).find((agent) => agent.target === "dsh-global") || null;
4015
- const autoLinkLabel = [t("links.dshGlobal"), ...autoLinkAgents.map((agent) => agent.label)].join("、");
3952
+ const exportTargetOptions = (data?.agents?.detected || []).filter((agent) => agent.kind === "agent" && agent.exportable);
3953
+ const exportSelection = data?.settings?.exportTargets;
3954
+ const autoLinkAgents = Array.isArray(exportSelection) ? exportTargetOptions.filter((agent) => exportSelection.includes(agent.target)) : [];
3955
+ const autoLinkLabel = autoLinkAgents.length > 0 ? autoLinkAgents.map((agent) => agent.label).join("、") : t("exportDefault.none");
4016
3956
  const placement = (placementOverride ?? data?.settings?.placement) === "sidebar" ? "sidebar" : "settings";
4017
3957
  function listScroller() {
4018
3958
  const node = rootRef.current?.querySelector?.(".dshsc-listscroll");
@@ -4060,7 +4000,7 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
4060
4000
  }
4061
4001
  async function saveAutoLink(targets) {
4062
4002
  setModal(null);
4063
- await run("settings", () => apiCall("POST", "/settings", { autoLinkTargets: targets }));
4003
+ await run("settings", () => apiCall("POST", "/settings", { exportTargets: targets }));
4064
4004
  }
4065
4005
  const installedActions = {
4066
4006
  onView: (row) => openViewer(row),
@@ -4068,7 +4008,7 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
4068
4008
  onToggleMode: actions.toggleMode,
4069
4009
  onUpdate: actions.requestUpdate,
4070
4010
  onDelete: actions.requestDelete,
4071
- onLinks: (row) => setModal({ type: "links", row }),
4011
+ onExports: (row) => setModal({ type: "exports", row }),
4072
4012
  onCheckUpdates: actions.checkUpdates,
4073
4013
  onUpdateAll: actions.updateAll,
4074
4014
  onAddSkill: actions.requestAddSkill,
@@ -4100,7 +4040,7 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
4100
4040
  onClose: closeViewer,
4101
4041
  onOpenPage: viewer.mode === "drawer" ? openPage : null,
4102
4042
  onUpdate: actions.requestUpdate,
4103
- onLinks: (row) => setModal({ type: "links", row }),
4043
+ onExports: (row) => setModal({ type: "exports", row }),
4104
4044
  onToast: (next) => setToast(next)
4105
4045
  }) : null;
4106
4046
  const listNode = h(
@@ -4112,7 +4052,7 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
4112
4052
  plugin: data?.plugin,
4113
4053
  writable,
4114
4054
  autoLinkLabel,
4115
- autoLinkCount: autoLinkAgents.length + 1,
4055
+ autoLinkCount: autoLinkAgents.length,
4116
4056
  tab,
4117
4057
  settingsOpen,
4118
4058
  settingsRef,
@@ -4177,14 +4117,13 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
4177
4117
  t,
4178
4118
  modal,
4179
4119
  data,
4180
- autoLinkAgents: autolinkTargets,
4181
- autoLinkDsh,
4120
+ autoLinkAgents: exportTargetOptions,
4182
4121
  pickDirectory,
4183
4122
  actions: {
4184
4123
  probeSkills: actions.probeSkills,
4185
4124
  installSkills: actions.installSkills,
4186
4125
  addSubscription: actions.addSubscription,
4187
- saveLinks: actions.saveLinks
4126
+ saveExports: actions.saveExports
4188
4127
  },
4189
4128
  onClose: () => setModal(null),
4190
4129
  onSaveAutoLink: saveAutoLink