dsh-agent-skills 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,14 +6,16 @@
6
6
 
7
7
  把 Claude Code、Codex、Gemini CLI、Cursor 等工具的本地 Agent Skills 接入 DeepSeek Harness,并在「设置 → Agent Skills」里统一查看、启停和管理。
8
8
 
9
+ ![Agent Skills 设置页面](https://raw.githubusercontent.com/minivv/dsh-agent-skills/main/agent-skills-page.png)
10
+
9
11
  ## 它解决什么问题
10
12
 
11
13
  很多 AI 编程工具都使用 `<技能名>/SKILL.md`,但技能分散在不同目录。`dsh-agent-skills` 会自动发现这些目录,把有效技能汇总进 DSH,并提供一个可视化设置页:
12
14
 
13
- - 自动发现 `~/.agents/skills`、`~/.claude/skills`、`~/.codex/skills`、`~/.gemini/skills` 等常用目录。
15
+ - 默认扫描 `~/.agents/skills`、`~/.claude/skills`、`~/.codex/skills`、`~/.config/opencode/skills` 和 `~/.gemini/skills`。
14
16
  - 支持添加任意自定义技能目录。
15
17
  - 显示每个来源是否存在、包含多少有效技能,以及技能名称和描述。
16
- - 可按目录或单个技能启停;变更会持久化,并在下一次 Agent step 生效。
18
+ - 可按目录或单个技能启停;变更会持久化,页面会提示刷新后生效。
17
19
  - 监听技能文件变化,也可随时手动重新扫描。
18
20
  - 不修改原始 `SKILL.md` 文件。
19
21
 
@@ -21,9 +23,9 @@
21
23
 
22
24
  ### 方式一:DSH 插件市场(推荐)
23
25
 
24
- 打开 DSH 的「设置 → 插件市场」,搜索 **Agent Skills**,点击安装。市场会优先使用 npm 包,安装速度快,也不需要在本机重新构建。
26
+ 通过 [DSH 插件市场](https://github.com/dsh-market/dsh-market) 安装,或打开 DSH 的「设置 → 插件市场」,搜索 **Agent Skills**,点击安装。市场会优先使用 npm 包,安装速度快,也不需要在本机重新构建。
25
27
 
26
- 安装后重启一次 DSH Web,再打开「设置 Agent Skills」。
28
+ 安装后继续完成下面的「开启接管 DSH 技能」步骤,再重启 DSH Web。
27
29
 
28
30
  ### 方式二:从 npm 安装
29
31
 
@@ -31,22 +33,32 @@
31
33
  dsh plugin --profile web add dsh-agent-skills
32
34
  ```
33
35
 
34
- 为了让技能开关覆盖 `standard` `code` Agent preset 中的 filesystem provider,还需安装一次 preset-only 行:
36
+ ### 方式三:从 GitHub 安装
35
37
 
36
38
  ```bash
37
- DSH_INSTALL_ROOT="$(npm root -g)/@deepseek-ai/dsh" \
38
- node ~/.dsh/profiles/web/node_modules/dsh-agent-skills/scripts/install-preset.mjs
39
+ dsh plugin --profile web add github:minivv/dsh-agent-skills
39
40
  ```
40
41
 
41
- 然后重启 DSH Web。这个 preset 入口只注册 scoped skill provider,不会在 Agent preset 中创建 `agentSkills` host 服务。
42
+ 仓库包含 `prepare: npm run build`,因此 pnpm 会在 Git 安装时自动构建。DSH/pnpm 可能会要求你授权构建脚本;不想授权构建时请使用 npm 或插件市场版本,npm 包已经包含构建好的 `lib/`。
43
+
44
+ ### 开启接管 DSH 技能
42
45
 
43
- ### 方式三:从 GitHub 安装
46
+ 完成任一种安装方式后:
47
+
48
+ 1. 打开 DSH 的「设置 → Agent Skills」。
49
+ 2. 点击 **开启接管 DSH 技能**。
50
+ 3. 看到“已开启”提示后重启 DSH Web。
51
+
52
+ 开启后,页面中的目录和技能开关会接入 `standard` 与 `code` preset,真正控制 Agent 可见的技能。这个操作只添加 `dsh-agent-skills/preset` provider,不会创建第二个 `agentSkills` host 服务。
53
+
54
+ 如果页面无法定位 DSH 安装位置,可以使用备用命令:
44
55
 
45
56
  ```bash
46
- dsh plugin --profile web add github:minivv/dsh-agent-skills
57
+ DSH_INSTALL_ROOT="$(npm root -g)/@deepseek-ai/dsh" \
58
+ node ~/.dsh/profiles/web/node_modules/dsh-agent-skills/scripts/install-preset.mjs
47
59
  ```
48
60
 
49
- 仓库包含 `prepare: npm run build`,因此 pnpm 会在 Git 安装时自动构建。DSH/pnpm 可能会要求你授权构建脚本;不想授权构建时请使用 npm 或插件市场版本,npm 包已经包含构建好的 `lib/`。
61
+ 执行后重启 DSH Web。DSH 升级可能覆盖内置 preset;如果页面重新显示“开启接管 DSH 技能”,点击一次即可恢复。
50
62
 
51
63
  ## 使用
52
64
 
@@ -54,7 +66,8 @@ dsh plugin --profile web add github:minivv/dsh-agent-skills
54
66
  2. 进入「设置 → Agent Skills」。
55
67
  3. 检查自动发现的来源,或点击「+ 添加目录」。
56
68
  4. 使用来源开关或技能开关控制 Agent 可见范围。
57
- 5. 修改磁盘上的技能后,点击「重新扫描」或等待自动刷新。
69
+ 5. 技能区出现“n 个技能变更需刷新页面生效”后,点击「刷新页面」。
70
+ 6. 修改磁盘上的技能后,点击「重新扫描」或等待自动刷新。
58
71
 
59
72
  插件状态保存在:
60
73
 
@@ -115,13 +128,14 @@ npm pack --dry-run
115
128
  ## 已知约束
116
129
 
117
130
  - DSH 技能名必须匹配 `[a-z0-9]+(?:-[a-z0-9]+)*`。
118
- - 修改插件安装或 preset 组合后需要重启一次;技能启停和重新扫描不需要重启。
119
- - DSH 升级可能覆盖其内置 preset 文件;如开关不再生效,重新运行 `scripts/install-preset.mjs`。
131
+ - 修改插件安装或 preset 组合后需要重启一次;目录或技能开关变更后按页面提示刷新。
132
+ - DSH 升级可能覆盖其内置 preset 文件;如开关不再生效,在设置页重新点击「开启接管 DSH 技能」。
120
133
 
121
134
  ## 相关链接
122
135
 
123
136
  - [GitHub](https://github.com/minivv/dsh-agent-skills)
124
137
  - [npm](https://www.npmjs.com/package/dsh-agent-skills)
138
+ - [DSH 插件市场](https://github.com/dsh-market/dsh-market)
125
139
  - [WeiSpot 项目页](https://weispot.vercel.app/projects/dsh-agent-skills)
126
140
 
127
141
  ## License
package/lib/client.js CHANGED
@@ -36,6 +36,14 @@ var zh = {
36
36
  nav: "Agent Skills",
37
37
  title: "Agent Skills",
38
38
  subtitle: "\u6D4F\u89C8\u3001\u542F\u7528\u548C\u7BA1\u7406\u667A\u80FD\u4F53\u6280\u80FD\u4E0E\u6765\u6E90\u3002",
39
+ takeoverTitle: "\u63A5\u7BA1 DSH \u6280\u80FD",
40
+ takeoverDescription: "\u5F00\u542F\u540E\uFF0C\u8FD9\u91CC\u7684\u76EE\u5F55\u548C\u6280\u80FD\u5F00\u5173\u4F1A\u771F\u6B63\u63A7\u5236 Agent \u53EF\u89C1\u7684\u6280\u80FD\u3002",
41
+ takeoverEnabledTitle: "\u5DF2\u63A5\u7BA1 DSH \u6280\u80FD",
42
+ takeoverEnabledDescription: "\u76EE\u5F55\u548C\u6280\u80FD\u5F00\u5173\u5DF2\u63A5\u5165 standard \u4E0E code \u9884\u8BBE\u3002",
43
+ takeoverRestartDescription: "\u5DF2\u5F00\u542F\uFF0C\u8BF7\u91CD\u542F DSH \u540E\u751F\u6548\u3002",
44
+ takeoverPartialDescription: "\u5DF2\u63A5\u5165 {configured}/{total} \u4E2A\u9884\u8BBE\uFF0C\u70B9\u51FB\u7EE7\u7EED\u5B8C\u6210\u3002",
45
+ takeoverUnavailableDescription: "\u672A\u627E\u5230 DSH \u5B89\u88C5\u4F4D\u7F6E\uFF0C\u8BF7\u786E\u8BA4\u5F53\u524D\u9875\u9762\u7531 DSH \u542F\u52A8\u540E\u91CD\u8BD5\u3002",
46
+ takeoverAction: "\u5F00\u542F\u63A5\u7BA1 DSH \u6280\u80FD",
39
47
  searchPlaceholder: "\u641C\u7D22\u6280\u80FD\u540D\u79F0\u6216\u63CF\u8FF0\u2026",
40
48
  sources: "\u6765\u6E90",
41
49
  validCount: "\u4E2A\u6709\u6548",
@@ -51,7 +59,10 @@ var zh = {
51
59
  hideSkills: "\u6536\u8D77\u6280\u80FD",
52
60
  noSkillsInDir: "\u8BE5\u76EE\u5F55\u4E0B\u6CA1\u6709\u53EF\u7528\u6280\u80FD",
53
61
  dirMissing: "\u76EE\u5F55\u4E0D\u5B58\u5728",
54
- enabledHeader: "\u6280\u80FD {total} \u4E2A\u53EF\u7528 \xB7 {custom} \u4E2A\u81EA\u5B9A\u4E49 \xB7 {global} \u4E2A\u5168\u5C40 \xB7 {builtin} \u4E2A\u5185\u7F6E",
62
+ enabledLabel: "\u6280\u80FD",
63
+ enabledStats: "{total} \u4E2A\u53EF\u7528 \xB7 {custom} \u4E2A\u81EA\u5B9A\u4E49 \xB7 {global} \u4E2A\u5168\u5C40 \xB7 {builtin} \u4E2A\u5185\u7F6E",
64
+ refreshPending: "{n} \u4E2A\u6280\u80FD\u53D8\u66F4\u9700\u5237\u65B0\u9875\u9762\u751F\u6548",
65
+ refreshPage: "\u5237\u65B0\u9875\u9762",
55
66
  noSkills: "\u5F53\u524D\u6CA1\u6709\u53EF\u7528\u7684\u6280\u80FD\u3002\u70B9\u51FB\u300C+ \u6DFB\u52A0\u76EE\u5F55\u300D\u6DFB\u52A0\u4E00\u4E2A\u6280\u80FD\u76EE\u5F55\uFF0C\u6216\u542F\u7528\u66F4\u591A\u6765\u6E90\u3002",
56
67
  noMatch: "\u6CA1\u6709\u5339\u914D\u7684\u6280\u80FD",
57
68
  tagCustom: "\u81EA\u5B9A\u4E49",
@@ -75,6 +86,14 @@ var en = {
75
86
  nav: "Agent Skills",
76
87
  title: "Agent Skills",
77
88
  subtitle: "Browse, enable, and manage agent skills and their sources.",
89
+ takeoverTitle: "Take over DSH skills",
90
+ takeoverDescription: "Connect these source and skill switches to the skills visible to the Agent.",
91
+ takeoverEnabledTitle: "DSH skills are managed here",
92
+ takeoverEnabledDescription: "Source and skill switches are connected to the standard and code presets.",
93
+ takeoverRestartDescription: "Enabled. Restart DSH to apply the change.",
94
+ takeoverPartialDescription: "Connected to {configured}/{total} presets. Continue to finish setup.",
95
+ takeoverUnavailableDescription: "DSH could not be located. Open this page from DSH and try again.",
96
+ takeoverAction: "Enable DSH skill takeover",
78
97
  searchPlaceholder: "Search skills by name or description\u2026",
79
98
  sources: "Sources",
80
99
  validCount: "valid",
@@ -90,7 +109,10 @@ var en = {
90
109
  hideSkills: "Hide skills",
91
110
  noSkillsInDir: "No usable skills in this directory",
92
111
  dirMissing: "directory missing",
93
- enabledHeader: "{total} skills available \xB7 {custom} custom \xB7 {global} global \xB7 {builtin} builtin",
112
+ enabledLabel: "Skills",
113
+ enabledStats: "{total} available \xB7 {custom} custom \xB7 {global} global \xB7 {builtin} builtin",
114
+ refreshPending: "{n} skill changes need a page refresh to take effect",
115
+ refreshPage: "Refresh page",
94
116
  noSkills: "No skills available. Click \u201C+ Add directory\u201D to add a skill directory, or enable more sources.",
95
117
  noMatch: "No matching skills",
96
118
  tagCustom: "Custom",
@@ -129,13 +151,13 @@ function SkillDescription({ text, t }) {
129
151
  const element = ref.current;
130
152
  if (element === null) return;
131
153
  const check2 = () => {
132
- setOverflow(element.scrollHeight > element.clientHeight + 1);
154
+ if (!expanded) setOverflow(element.scrollHeight > element.clientHeight + 1);
133
155
  };
134
156
  check2();
135
157
  const observer = new ResizeObserver(check2);
136
158
  observer.observe(element);
137
159
  return () => observer.disconnect();
138
- }, [text]);
160
+ }, [expanded, text]);
139
161
  if (text === "") return null;
140
162
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
141
163
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { ref, className: expanded ? "as-skill-desc" : "as-skill-desc as-skill-desc-clamped", children: text }),
@@ -180,6 +202,8 @@ function DirCard({
180
202
  }
181
203
  function AgentSkillsSection({ api, t }) {
182
204
  const [view, setView] = (0, import_react.useState)(void 0);
205
+ const [takeover, setTakeover] = (0, import_react.useState)(void 0);
206
+ const [takeoverRestartRequired, setTakeoverRestartRequired] = (0, import_react.useState)(false);
183
207
  const [busy, setBusy] = (0, import_react.useState)(true);
184
208
  const [error51, setError] = (0, import_react.useState)("");
185
209
  const [query, setQuery] = (0, import_react.useState)("");
@@ -188,31 +212,61 @@ function AgentSkillsSection({ api, t }) {
188
212
  const [addDirText, setAddDirText] = (0, import_react.useState)("");
189
213
  const [dirExpanded, setDirExpanded] = (0, import_react.useState)(/* @__PURE__ */ new Set());
190
214
  const [saving, setSaving] = (0, import_react.useState)(false);
215
+ const [pendingSkillCount, setPendingSkillCount] = (0, import_react.useState)(0);
216
+ const baselineEnabled = (0, import_react.useRef)(void 0);
217
+ const rememberBaseline = (0, import_react.useCallback)((next) => {
218
+ if (baselineEnabled.current !== void 0) return;
219
+ baselineEnabled.current = new Map(next.skills.map((skill) => [skill.name, skill.enabled]));
220
+ }, []);
221
+ const updatePendingCount = (0, import_react.useCallback)((next) => {
222
+ const baseline = baselineEnabled.current;
223
+ if (baseline === void 0) {
224
+ rememberBaseline(next);
225
+ setPendingSkillCount(0);
226
+ return;
227
+ }
228
+ let changed = 0;
229
+ for (const skill of next.skills) {
230
+ const initial = baseline.get(skill.name);
231
+ if (initial !== void 0 && initial !== skill.enabled) changed += 1;
232
+ }
233
+ setPendingSkillCount(changed);
234
+ }, [rememberBaseline]);
191
235
  const load = (0, import_react.useCallback)(async (refresh = () => api.list()) => {
192
236
  setError("");
193
237
  const result2 = await refresh();
194
238
  if (result2.ok) {
239
+ rememberBaseline(result2.value);
195
240
  setView(result2.value);
196
241
  } else {
197
242
  setError(t("errorLoad", { message: result2.error.message }));
198
243
  }
244
+ }, [api, rememberBaseline, t]);
245
+ const loadTakeover = (0, import_react.useCallback)(async (refresh = () => api.takeoverStatus()) => {
246
+ const result2 = await refresh();
247
+ if (result2.ok) {
248
+ setTakeover(result2.value);
249
+ } else {
250
+ setError(t("errorLoad", { message: result2.error.message }));
251
+ }
199
252
  }, [api, t]);
200
253
  (0, import_react.useEffect)(() => {
201
254
  setBusy(true);
202
- void load().finally(() => setBusy(false));
203
- }, [load]);
255
+ void Promise.all([load(), loadTakeover()]).finally(() => setBusy(false));
256
+ }, [load, loadTakeover]);
204
257
  (0, import_react.useEffect)(() => {
205
258
  const timer = window.setInterval(() => {
206
259
  void load();
207
260
  }, 5e3);
208
261
  return () => window.clearInterval(timer);
209
262
  }, [load]);
210
- const mutate = (0, import_react.useCallback)(async (operation) => {
263
+ const mutate = (0, import_react.useCallback)(async (operation, trackSkillChanges = false) => {
211
264
  setSaving(true);
212
265
  setError("");
213
266
  try {
214
267
  const result2 = await operation();
215
268
  if (result2.ok) {
269
+ if (trackSkillChanges) updatePendingCount(result2.value);
216
270
  setView(result2.value);
217
271
  } else {
218
272
  setError(t("errorSave", { message: result2.error.message }));
@@ -220,7 +274,22 @@ function AgentSkillsSection({ api, t }) {
220
274
  } finally {
221
275
  setSaving(false);
222
276
  }
223
- }, [t]);
277
+ }, [t, updatePendingCount]);
278
+ const enableTakeover = (0, import_react.useCallback)(async () => {
279
+ setSaving(true);
280
+ setError("");
281
+ try {
282
+ const result2 = await api.enableTakeover();
283
+ if (result2.ok) {
284
+ setTakeover(result2.value);
285
+ setTakeoverRestartRequired(result2.value.enabled);
286
+ } else {
287
+ setError(t("errorSave", { message: result2.error.message }));
288
+ }
289
+ } finally {
290
+ setSaving(false);
291
+ }
292
+ }, [api, t]);
224
293
  const toggleDirExpanded = (path) => {
225
294
  setDirExpanded((prev) => {
226
295
  const next = new Set(prev);
@@ -265,6 +334,14 @@ function AgentSkillsSection({ api, t }) {
265
334
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "as-wrap", "data-plugin": "dsh-agent-skills", children: [
266
335
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "as-title", children: t("title") }),
267
336
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "as-subtitle", children: t("subtitle") }),
337
+ takeover !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: takeover.enabled ? "as-takeover as-takeover-enabled" : "as-takeover", children: [
338
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "as-takeover-icon", "aria-hidden": "true", children: takeover.enabled ? "\u2713" : "\u2192" }),
339
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "as-takeover-copy", children: [
340
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: takeover.enabled ? t("takeoverEnabledTitle") : t("takeoverTitle") }),
341
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: takeoverRestartRequired ? t("takeoverRestartDescription") : takeover.enabled ? t("takeoverEnabledDescription") : takeover.available && takeover.configured > 0 ? t("takeoverPartialDescription", { configured: takeover.configured, total: takeover.total }) : takeover.available ? t("takeoverDescription") : t("takeoverUnavailableDescription") })
342
+ ] }),
343
+ !takeover.enabled && takeover.available && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "as-btn as-btn-primary as-takeover-action", disabled: saving, onClick: () => void enableTakeover(), children: t("takeoverAction") })
344
+ ] }),
268
345
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
269
346
  "input",
270
347
  {
@@ -376,17 +453,25 @@ function AgentSkillsSection({ api, t }) {
376
453
  t,
377
454
  expanded: dirExpanded.has(dir.path),
378
455
  onToggleExpanded: () => toggleDirExpanded(dir.path),
379
- onToggleEnabled: (next) => void mutate(() => api.toggleDir({ path: dir.path, enabled: next })),
456
+ onToggleEnabled: (next) => void mutate(() => api.toggleDir({ path: dir.path, enabled: next }), true),
380
457
  onRemove: () => void mutate(() => api.removeDir({ path: dir.path }))
381
458
  },
382
459
  dir.path
383
460
  )) }),
384
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "as-skills-header", children: t("enabledHeader", {
385
- total: view.counts.total,
386
- custom: view.counts.custom,
387
- global: view.counts.global,
388
- builtin: view.counts.builtin
389
- }) }),
461
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", { className: "as-skills-header", children: [
462
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "as-skills-header-title", children: t("enabledLabel") }),
463
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "as-skills-header-stats", children: t("enabledStats", {
464
+ total: view.counts.total,
465
+ custom: view.counts.custom,
466
+ global: view.counts.global,
467
+ builtin: view.counts.builtin
468
+ }) })
469
+ ] }),
470
+ pendingSkillCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "as-refresh-card", role: "status", children: [
471
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "as-refresh-icon", "aria-hidden": "true", children: "\u21BB" }),
472
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "as-refresh-message", children: t("refreshPending", { n: pendingSkillCount }) }),
473
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "as-btn as-refresh-action", onClick: () => window.location.reload(), children: t("refreshPage") })
474
+ ] }),
390
475
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "as-skill-grid", children: filteredSkills.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "as-empty", children: query.trim() === "" ? t("noSkills") : t("noMatch") }) : filteredSkills.map((skill) => {
391
476
  const tagClass = skill.kind === "custom" ? "as-tag-custom" : skill.kind === "builtin" ? "as-tag-builtin" : "as-tag-global";
392
477
  const tagLabel = skill.kind === "custom" ? t("tagCustom") : skill.kind === "builtin" ? t("tagBuiltin") : t("tagGlobal");
@@ -403,7 +488,7 @@ function AgentSkillsSection({ api, t }) {
403
488
  {
404
489
  checked: skill.enabled,
405
490
  disabled: saving || !skill.toggleable,
406
- onChange: (next) => void mutate(() => api.toggleSkill({ name: skill.name, enabled: next })),
491
+ onChange: (next) => void mutate(() => api.toggleSkill({ name: skill.name, enabled: next }), true),
407
492
  label: skill.enabled ? t("enabled") : t("disabled")
408
493
  }
409
494
  )
@@ -428,8 +513,11 @@ var CSS = `
428
513
  --as-accent: #3fb950;
429
514
  --as-accent-soft: rgba(63, 185, 80, 0.15);
430
515
  --as-danger: #f85149;
431
- --as-radius: 10px;
516
+ --as-warning: #d29922;
517
+ --as-warning-soft: rgba(210, 153, 34, 0.12);
518
+ --as-radius: 8px;
432
519
  color-scheme: light dark;
520
+ -webkit-font-smoothing: antialiased;
433
521
  }
434
522
 
435
523
  .as-wrap { display: flex; flex-direction: column; gap: 14px; width: 100%; max-width: 860px; }
@@ -438,6 +526,16 @@ var CSS = `
438
526
  .as-search { width: 100%; box-sizing: border-box; padding: 8px 12px; border-radius: 8px; border: 1px solid var(--as-border); background: transparent; color: inherit; font-size: 13px; outline: none; }
439
527
  .as-search:focus { border-color: var(--as-accent); }
440
528
 
529
+ .as-takeover { display: flex; align-items: center; gap: 10px; min-height: 48px; padding: 9px 10px; border: 1px solid var(--as-border-soft); border-radius: var(--as-radius); background: rgba(128, 128, 128, 0.04); box-sizing: border-box; }
530
+ .as-takeover-enabled { border-color: rgba(63, 185, 80, 0.28); background: rgba(63, 185, 80, 0.07); }
531
+ .as-takeover-icon { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; flex: none; border-radius: 50%; background: rgba(128, 128, 128, 0.13); color: var(--as-muted); font-size: 14px; font-weight: 650; }
532
+ .as-takeover-enabled .as-takeover-icon { color: var(--as-accent); background: var(--as-accent-soft); }
533
+ .as-takeover-copy { display: flex; flex: 1; min-width: 0; flex-direction: column; gap: 2px; }
534
+ .as-takeover-copy strong { font-size: 13px; font-weight: 600; line-height: 1.35; text-wrap: balance; }
535
+ .as-takeover-copy span { font-size: 12px; line-height: 1.45; color: var(--as-muted); text-wrap: pretty; }
536
+ .as-takeover-action { min-height: 40px; flex: none; transition-property: transform, background-color; transition-duration: 120ms; }
537
+ .as-takeover-action:active:not(:disabled) { transform: scale(0.96); }
538
+
441
539
  .as-sources-row { display: flex; align-items: baseline; gap: 10px; }
442
540
  .as-sources-label { font-size: 14px; font-weight: 550; }
443
541
  .as-sources-counts { font-size: 12px; color: var(--as-muted); }
@@ -481,8 +579,15 @@ var CSS = `
481
579
  .as-dir-skill-name { font-size: 13px; font-weight: 550; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
482
580
  .as-dir-skill-desc { font-size: 12px; color: var(--as-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
483
581
 
484
- .as-skills-header { font-size: 13px; font-weight: 550; margin: 4px 0 0; }
485
- .as-skills-header .muted { font-weight: 450; color: var(--as-muted); }
582
+ .as-skills-header { display: flex; align-items: baseline; gap: 7px; font-size: 13px; font-weight: 550; margin: 4px 0 0; text-wrap: balance; }
583
+ .as-skills-header-title { color: var(--as-text); }
584
+ .as-skills-header-stats { font-size: 11px; font-weight: 450; color: var(--as-muted-2); font-variant-numeric: tabular-nums; }
585
+ .as-refresh-card { display: flex; align-items: center; gap: 9px; min-height: 48px; padding: 4px 5px 4px 11px; border: 1px solid rgba(210, 153, 34, 0.28); border-radius: var(--as-radius); background: var(--as-warning-soft); box-sizing: border-box; }
586
+ .as-refresh-icon { flex: none; color: var(--as-warning); font-size: 17px; line-height: 1; }
587
+ .as-refresh-message { flex: 1; min-width: 0; color: var(--as-text); font-size: 12px; line-height: 1.45; font-variant-numeric: tabular-nums; text-wrap: pretty; }
588
+ .as-refresh-action { min-height: 40px; flex: none; border-color: rgba(210, 153, 34, 0.42); background: rgba(210, 153, 34, 0.08); transition-property: transform, background-color; transition-duration: 120ms; }
589
+ .as-refresh-action:hover { background: rgba(210, 153, 34, 0.14); }
590
+ .as-refresh-action:active { transform: scale(0.96); }
486
591
  .as-skill-grid { display: flex; flex-direction: column; gap: 8px; }
487
592
  .as-skill-card { border: 1px solid var(--as-border-soft); border-radius: var(--as-radius); padding: 10px 12px; display: flex; flex-direction: column; gap: 5px; }
488
593
  .as-skill-row { display: flex; align-items: center; gap: 8px; }
@@ -505,6 +610,43 @@ var CSS = `
505
610
  .as-empty { font-size: 12px; color: var(--as-muted); padding: 12px 0; }
506
611
  .as-error { font-size: 12px; color: var(--as-danger); }
507
612
  .as-busy { font-size: 12px; color: var(--as-muted-2); }
613
+
614
+ @media (max-width: 640px) {
615
+ [role="dialog"]:has([data-plugin="dsh-agent-skills"]) { flex-direction: column; }
616
+ [role="dialog"]:has([data-plugin="dsh-agent-skills"]) > nav {
617
+ width: 100%;
618
+ height: auto;
619
+ flex: none;
620
+ box-sizing: border-box;
621
+ border-right: 0;
622
+ border-bottom: 1px solid var(--as-border-soft);
623
+ }
624
+ [role="dialog"]:has([data-plugin="dsh-agent-skills"]) > nav > :first-child { display: none; }
625
+ [role="dialog"]:has([data-plugin="dsh-agent-skills"]) > nav > :last-child {
626
+ display: grid;
627
+ grid-template-columns: repeat(2, minmax(0, 1fr));
628
+ gap: 4px;
629
+ }
630
+ [role="dialog"]:has([data-plugin="dsh-agent-skills"]) > nav > :last-child > button {
631
+ min-width: 0;
632
+ padding-inline: 8px;
633
+ }
634
+ [role="dialog"]:has([data-plugin="dsh-agent-skills"]) > div {
635
+ width: 100%;
636
+ min-width: 0;
637
+ min-height: 0;
638
+ flex: 1;
639
+ }
640
+ .as-takeover { align-items: flex-start; flex-wrap: wrap; }
641
+ .as-takeover-action { width: 100%; }
642
+ .as-actions-row { flex-wrap: wrap; }
643
+ .as-dir-row { align-items: flex-start; flex-wrap: wrap; }
644
+ .as-dir-path { flex: 1; min-width: 120px; }
645
+ .as-skill-row { align-items: flex-start; flex-wrap: wrap; }
646
+ .as-refresh-card { flex-wrap: wrap; padding: 8px 9px; }
647
+ .as-refresh-message { min-width: calc(100% - 34px); }
648
+ .as-refresh-action { width: 100%; }
649
+ }
508
650
  `;
509
651
  var installed;
510
652
  function injectStyles() {
@@ -15092,6 +15234,16 @@ var agentSkillsViewSchema = external_exports.object({
15092
15234
  validDirs: external_exports.number().int().min(0),
15093
15235
  missingDirs: external_exports.number().int().min(0)
15094
15236
  });
15237
+ var presetTakeoverStatusSchema = external_exports.object({
15238
+ /** Whether a verified DSH install with supported presets was found. */
15239
+ available: external_exports.boolean(),
15240
+ /** Whether every available standard/code preset contains the managed row. */
15241
+ enabled: external_exports.boolean(),
15242
+ /** Number of preset files that already contain the managed row. */
15243
+ configured: external_exports.number().int().min(0),
15244
+ /** Number of supported preset files found in this DSH installation. */
15245
+ total: external_exports.number().int().min(0)
15246
+ });
15095
15247
  var toggleSkillInputSchema = external_exports.object({
15096
15248
  name: external_exports.string(),
15097
15249
  enabled: external_exports.boolean()
@@ -15128,6 +15280,24 @@ var TYPERT_REMOTE = {
15128
15280
  parameters: [],
15129
15281
  result: result("AgentSkillsView", agentSkillsViewSchema)
15130
15282
  },
15283
+ {
15284
+ id: `${PKG}#agentSkills/takeoverStatus`,
15285
+ service: "agentSkills",
15286
+ namespace: "agentSkills",
15287
+ method: "takeoverStatus",
15288
+ invocation: direct,
15289
+ parameters: [],
15290
+ result: result("PresetTakeoverStatus", presetTakeoverStatusSchema)
15291
+ },
15292
+ {
15293
+ id: `${PKG}#agentSkills/enableTakeover`,
15294
+ service: "agentSkills",
15295
+ namespace: "agentSkills",
15296
+ method: "enableTakeover",
15297
+ invocation: direct,
15298
+ parameters: [],
15299
+ result: result("PresetTakeoverStatus", presetTakeoverStatusSchema)
15300
+ },
15131
15301
  {
15132
15302
  id: `${PKG}#agentSkills/toggleSkill`,
15133
15303
  service: "agentSkills",