dsh-skill-browser 1.6.2 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -3,22 +3,29 @@
3
3
  // user-configurable per machine - see detectRoot), category tree, Chinese
4
4
  // one-liners, full SKILL.md bodies, and the skill-failure ledger engine.
5
5
  // Pure host service; visual work lives in lib/client.js.
6
+ // v1.7.0/1.7.1/1.7.2/1.7.3 rebuild (2026-09-12):
7
+ // - POST /toggle {dir, disable?} -> setSkillDisabled on one skill.
8
+ // - POST /toggle-batch {dirs:[...]} | {category}, disable? -> batch toggling.
9
+ // - GET /skills now carries `ui` (lib/zh.js UI_TEXT) and per-skill `disabled`.
6
10
  import { existsSync, readFileSync, mkdirSync, writeFileSync, appendFileSync, readdirSync } from "node:fs";
7
11
  import { join, dirname } from "node:path";
8
12
  import { homedir } from "node:os";
9
13
  import { spawn } from "node:child_process";
10
- import { scanSkills, categoriesMeta, readSkillBody, parseLedgerTable, parseLedgerDetail, findLedgerFile, readLedgerAtPath, ledgerDefaultPath } from "./scan.js";
14
+ import { scanSkills, categoriesMeta, readSkillBody, parseLedgerTable, parseLedgerDetail, findLedgerFile, readLedgerAtPath, ledgerDefaultPath, setSkillDisabled } from "./scan.js";
11
15
  import {
12
16
  createLedgerState, setLedgerRoot, setLedgerPath, recordSkillResult, reportFailure,
13
17
  flushNow, initializeLedger, ledgerFilePath, autoLogFilePath, readAutoLog
14
18
  } from "./ledger-writer.js";
15
- import { ZH } from "./zh.js";
19
+ import { ZH, UI_TEXT } from "./zh.js";
16
20
 
17
21
  var PREFIX = "/dsh-skill-browser";
18
- var VERSION = "1.6.0";
22
+ var VERSION = "1.7.4";
19
23
  var CACHE_TTL_MS = 30 * 1000;
20
24
  var DIAG_FILE = join(homedir(), ".dsh", "data", "dsh-skill-browser", "diag.log");
21
25
  var CONFIG_FILE = join(homedir(), ".dsh", "data", "dsh-skill-browser", "config.json");
26
+ // v1.7.2: hard cap on one batch call (skill libraries are ~hundreds, this is
27
+ // only an abuse guard - the panel never sends more than the full library).
28
+ var BATCH_MAX = 2000;
22
29
 
23
30
  export var name = "dsh-skill-browser";
24
31
  export var inject = ["webServer"];
@@ -212,7 +219,10 @@ export function apply(ctx, config) {
212
219
  total: skills.length,
213
220
  generatedAt: new Date().toISOString(),
214
221
  categories: categories,
215
- skills: skills
222
+ skills: skills,
223
+ // v1.7.1/1.7.2: 面板 UI 文案(客户端 T()/TF() 优先取用,缺失时走
224
+ // client.js 内置 UI_FALLBACK 兜底)。
225
+ ui: UI_TEXT
216
226
  };
217
227
  }
218
228
 
@@ -260,6 +270,91 @@ export function apply(ctx, config) {
260
270
  return cacheLedger.payload;
261
271
  }
262
272
 
273
+ // ── v1.7.2 启用/停用 ──────────────────────────────────────
274
+ // POST /toggle {dir, disable?(缺省=true)} → 单技能启停,成功后使 /skills
275
+ // 缓存失效。响应:{ok:true, dir, changed, backup, disabled} / {ok:false, dir, error}。
276
+ function handleToggle(req, res) {
277
+ var chunks = [];
278
+ req.on("data", function (c) { chunks.push(c); });
279
+ req.on("end", function () {
280
+ var dir = "", wantOff = true;
281
+ try {
282
+ var body = JSON.parse(Buffer.concat(chunks).toString("utf8")) || {};
283
+ dir = String(body.dir || "");
284
+ wantOff = body.disable === undefined ? true : !!body.disable;
285
+ var r = setSkillDisabled(root, dir, wantOff);
286
+ if (r.ok) {
287
+ cache.payload = null; // 下次 /skills 重扫,拿到最新 disabled
288
+ ctx.logger.info("dsh-skill-browser: toggle " + dir + " -> " + (wantOff ? "disabled" : "enabled") +
289
+ " (changed=" + r.changed + ", backup=" + (r.backup || "none") + ")");
290
+ sendJson(res, 200, { ok: true, dir: dir, changed: r.changed, backup: r.backup, disabled: r.disabled });
291
+ } else {
292
+ ctx.logger.warn("dsh-skill-browser: toggle " + dir + " failed: " + r.error);
293
+ sendJson(res, 200, { ok: false, dir: dir, error: r.error });
294
+ }
295
+ } catch (e) {
296
+ sendJson(res, 200, { ok: false, dir: dir, error: String(e && e.message || e) });
297
+ }
298
+ });
299
+ }
300
+
301
+ // POST /toggle-batch {dirs:[...]} 或 {category:"<cat-id>"},可选 disable
302
+ // (缺省=true)→ 循环单技能启停,部分失败不影响其余。
303
+ // 响应:{ok:true, total, okCount, failCount, anyChanged, results:[{dir, ok, changed, error|null}]}。
304
+ function handleToggleBatch(req, res) {
305
+ var chunks = [];
306
+ req.on("data", function (c) { chunks.push(c); });
307
+ req.on("end", function () {
308
+ try {
309
+ var body = JSON.parse(Buffer.concat(chunks).toString("utf8")) || {};
310
+ var wantOff = body.disable === undefined ? true : !!body.disable;
311
+ var dirs = [];
312
+ if (Object.prototype.toString.call(body.dirs) === "[object Array]") {
313
+ for (var i = 0; i < body.dirs.length && dirs.length < BATCH_MAX; i++) {
314
+ var d = String(body.dirs[i] || "");
315
+ if (d && dirs.indexOf(d) < 0) dirs.push(d);
316
+ }
317
+ } else if (typeof body.category === "string" && body.category) {
318
+ // 分类批量:现扫一遍技能库取该分类全部目录(不走缓存,避免脏数据)
319
+ var skills = scanSkills(root, ZH);
320
+ for (var j = 0; j < skills.length; j++) {
321
+ if (skills[j].cat === body.category) dirs.push(skills[j].dir);
322
+ }
323
+ } else {
324
+ sendJson(res, 200, { ok: false, error: "need dirs:[...] or category:<cat-id>" });
325
+ return;
326
+ }
327
+ var results = [], okCount = 0, failCount = 0, anyChanged = false;
328
+ for (var k = 0; k < dirs.length; k++) {
329
+ var r = setSkillDisabled(root, dirs[k], wantOff);
330
+ if (r.ok) {
331
+ okCount++;
332
+ if (r.changed) anyChanged = true;
333
+ results.push({ dir: dirs[k], ok: true, changed: r.changed, error: null });
334
+ } else {
335
+ failCount++;
336
+ results.push({ dir: dirs[k], ok: false, changed: false, error: r.error });
337
+ }
338
+ }
339
+ if (anyChanged) cache.payload = null;
340
+ ctx.logger.info("dsh-skill-browser: toggle-batch " +
341
+ (body.category ? "category=" + body.category : "dirs=" + dirs.length) +
342
+ " -> " + (wantOff ? "disabled" : "enabled") +
343
+ " (ok=" + okCount + ", fail=" + failCount + ", changed=" + anyChanged + ")");
344
+ sendJson(res, 200, {
345
+ ok: true,
346
+ total: dirs.length,
347
+ okCount: okCount,
348
+ failCount: failCount,
349
+ anyChanged: anyChanged,
350
+ results: results
351
+ });
352
+ } catch (e) {
353
+ sendJson(res, 200, { ok: false, error: String(e && e.message || e) });
354
+ }
355
+ });
356
+ }
357
+
263
358
  ctx.effect(function () {
264
359
  var handler = function (req, res) {
265
360
  if (!isLoopback(req)) { res.writeHead(403); res.end(); return; }
@@ -351,6 +446,18 @@ export function apply(ctx, config) {
351
446
  return;
352
447
  }
353
448
 
449
+ // ── v1.7.2 单技能启用/停用 ──
450
+ if (path === PREFIX + "/toggle" && req.method === "POST") {
451
+ handleToggle(req, res);
452
+ return;
453
+ }
454
+
455
+ // ── v1.7.0 分类批量 / v1.7.2 全局批量启用停用 ──
456
+ if (path === PREFIX + "/toggle-batch" && req.method === "POST") {
457
+ handleToggleBatch(req, res);
458
+ return;
459
+ }
460
+
354
461
  if (path === PREFIX + "/ledger") {
355
462
  try {
356
463
  sendJson(res, 200, getLedgerPayload(/(^|&)refresh=1(&|$)/.test(query)));
package/lib/scan.js CHANGED
@@ -1,10 +1,25 @@
1
1
  // dsh-skill-browser - skill directory scanning & categorization (host side).
2
2
  // Pure functions, no DSH service dependencies, so it can be unit-tested
3
3
  // standalone with `node test/run.js`.
4
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
4
+ // v1.7.0/1.7.1/1.7.2 rebuild (2026-09-12):
5
+ // - parseFrontmatter tolerates a UTF-8 BOM before the opening `---`.
6
+ // - scanSkills emits `disabled` per skill (frontmatter disable-model-invocation).
7
+ // - new setSkillDisabled(root, dir, disable): enable/disable one skill by
8
+ // toggling the single `disable-model-invocation:` line inside the YAML
9
+ // frontmatter of its SKILL.md. Only that line is touched - CRLF/LF style
10
+ // and BOM state are preserved byte-for-byte; a pre-write backup
11
+ // SKILL.md.bak-toggle-<yyyyMMdd-HHmmss> is created and rolled back on
12
+ // write failure. Never throws - returns { ok, error?, changed, backup, disabled }.
13
+ import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
5
14
  import { join } from "node:path";
6
15
  import { CAT_RULES, SUB_RULES, CATS } from "./zh.js";
7
16
 
17
+ var DISABLE_KEY = "disable-model-invocation";
18
+ // Frontmatter value treated as "disabled = true" (what we write is `true`).
19
+ var DISABLE_TRUE_RE = /^(true|1|yes)$/i;
20
+ // Skill dir whitelist (same as readSkillBody) - blocks path traversal.
21
+ var SAFE_DIR_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
22
+
8
23
  // Parse a flat YAML frontmatter (name/description/...) into a plain object.
9
24
  // Handles folded scalars (`description: >` followed by indented lines) and
10
25
  // strips surrounding quotes. Not a full YAML parser - frontmatter here is flat.
@@ -33,6 +48,11 @@ export function parseFrontmatter(text) {
33
48
  return out;
34
49
  }
35
50
 
51
+ // Whether a parsed frontmatter object marks the skill as disabled.
52
+ function fmSaysDisabled(fm) {
53
+ return fm && DISABLE_TRUE_RE.test(String(fm[DISABLE_KEY] || "").trim());
54
+ }
55
+
36
56
  // Categorize a skill directory name: { id, sub }.
37
57
  export function categorize(dirName) {
38
58
  var cat = "other";
@@ -52,6 +72,7 @@ export function categorize(dirName) {
52
72
  // Scan one skill root directory. Returns an array of skill summaries,
53
73
  // sorted by directory name. Skips non-directories, `_`-prefixed and
54
74
  // `.`-prefixed entries, and directories without a SKILL.md.
75
+ // v1.7.2: each entry also carries `disabled` (read from frontmatter).
55
76
  export function scanSkills(root, zhDict) {
56
77
  var out = [];
57
78
  if (!root || !existsSync(root)) return out;
@@ -72,7 +93,8 @@ export function scanSkills(root, zhDict) {
72
93
  zh: (zhDict && zhDict[e.name]) || "",
73
94
  cat: cat.id,
74
95
  sub: cat.sub,
75
- mtime: safeMtime(skillFile)
96
+ mtime: safeMtime(skillFile),
97
+ disabled: fmSaysDisabled(fm)
76
98
  });
77
99
  }
78
100
  out.sort(function (a, b) { return a.dir < b.dir ? -1 : a.dir > b.dir ? 1 : 0; });
@@ -97,6 +119,162 @@ export function readSkillBody(root, dir) {
97
119
  return readFileSync(p, "utf8");
98
120
  }
99
121
 
122
+ // ── v1.7.2 技能启用/停用(写 frontmatter 单行)────────────────────
123
+ // 停用 = 在 SKILL.md frontmatter 中加/改 `disable-model-invocation: true`;
124
+ // 启用 = 删掉该行(frontmatter 因此变空则整块剥除还原)。
125
+ // 只动这一行:换行风格(CRLF/LF)与 BOM 状态保持;写前备份为
126
+ // SKILL.md.bak-toggle-<yyyyMMdd-HHmmss>;写失败自动回滚;绝不抛出。
127
+ // 返回 { ok, error?, dir, changed, backup, disabled }。
128
+ function pad2(n) { return n < 10 ? "0" + n : "" + n; }
129
+
130
+ function backupStamp() {
131
+ var d = new Date();
132
+ return "" + d.getFullYear() + pad2(d.getMonth() + 1) + pad2(d.getDate()) +
133
+ "-" + pad2(d.getHours()) + pad2(d.getMinutes()) + pad2(d.getSeconds());
134
+ }
135
+
136
+ // Locate the frontmatter block in a BOM-stripped text.
137
+ // Returns null when the file has no (complete) frontmatter block, else:
138
+ // contentStart/contentEnd - the YAML content between the two `---` lines
139
+ // (contentEnd = index of the newline that precedes the closing `---`)
140
+ // closeStart - index of the closing `---`
141
+ // blockEnd - index just after the closing `---` and its trailing EOL
142
+ // eol - EOL used right before the closing delimiter
143
+ function locateFrontmatter(text) {
144
+ var openM = /^---[ \t]*(\r?\n)/.exec(text);
145
+ if (!openM) return null;
146
+ var closeM = /(\r?\n)---[ \t]*(?:\r?\n|$)/.exec(text);
147
+ if (!closeM) return null;
148
+ var closeStart = closeM.index + closeM[1].length;
149
+ return {
150
+ contentStart: openM[0].length,
151
+ contentEnd: closeM.index,
152
+ closeStart: closeStart,
153
+ blockEnd: closeM.index + closeM[0].length,
154
+ eol: closeM[1] === "\r\n" ? "\r\n" : "\n"
155
+ };
156
+ }
157
+
158
+ // Find the `disable-model-invocation:` line inside a frontmatter content
159
+ // string. Returns null or { lineStart, lineEnd, value } with offsets
160
+ // relative to the content string; lineStart includes leading indentation,
161
+ // lineEnd excludes the trailing EOL.
162
+ function findDisableLine(content) {
163
+ var m = /(?:^|(\r?\n))([ \t]*)disable-model-invocation[ \t]*:([^\r\n]*)/.exec(content);
164
+ if (!m) return null;
165
+ var lineStart = m.index + (m[1] ? m[1].length : 0);
166
+ return {
167
+ lineStart: lineStart,
168
+ lineEnd: m.index + m[0].length,
169
+ value: m[3].trim().replace(/^["']|["']$/g, "")
170
+ };
171
+ }
172
+
173
+ export function setSkillDisabled(root, dir, disable) {
174
+ var out = { ok: false, dir: dir, error: null, changed: false, backup: null, disabled: false };
175
+ try {
176
+ if (typeof root !== "string" || !root) { out.error = "skill root not configured"; return out; }
177
+ if (typeof dir !== "string" || !SAFE_DIR_RE.test(dir) || dir.indexOf("..") >= 0) {
178
+ out.error = "invalid skill dir name: " + dir;
179
+ return out;
180
+ }
181
+ var p = join(root, dir, "SKILL.md");
182
+ if (!existsSync(p)) { out.error = "SKILL.md not found: " + p; return out; }
183
+
184
+ var raw = readFileSync(p, "utf8");
185
+ var bom = "";
186
+ var text = raw;
187
+ if (text.charCodeAt(0) === 0xfeff) { bom = "\uFEFF"; text = text.slice(1); }
188
+ var eol = text.indexOf("\r\n") >= 0 ? "\r\n" : "\n";
189
+ var fm = locateFrontmatter(text);
190
+ var wantOff = !!disable;
191
+ var nowDisabled = false;
192
+ var next = null; // BOM-stripped new text; null = no change needed
193
+
194
+ if (fm) {
195
+ var content = text.slice(fm.contentStart, fm.contentEnd);
196
+ var line = findDisableLine(content);
197
+ if (wantOff) {
198
+ if (!line) {
199
+ // Insert `disable-model-invocation: true` at the end of the block
200
+ // (right before the closing `---`). Column-0 key ends any folded
201
+ // scalar safely, and the preceding content always has its EOL.
202
+ next = text.slice(0, fm.closeStart) + DISABLE_KEY + ": true" + fm.eol + text.slice(fm.closeStart);
203
+ nowDisabled = true;
204
+ } else if (!DISABLE_TRUE_RE.test(line.value)) {
205
+ // Rewrite just that line (keep its indentation/EOL neighbours).
206
+ var lineStartAbs = fm.contentStart + line.lineStart;
207
+ var lineEndAbs = fm.contentStart + line.lineEnd;
208
+ next = text.slice(0, lineStartAbs) + DISABLE_KEY + ": true" + text.slice(lineEndAbs);
209
+ nowDisabled = true;
210
+ } else {
211
+ nowDisabled = true; // already disabled - idempotent no-change
212
+ }
213
+ } else {
214
+ if (!line) {
215
+ nowDisabled = false; // already enabled - idempotent no-change
216
+ } else {
217
+ // Remove the line plus its trailing EOL; when it is the block's
218
+ // last line (no trailing EOL) remove the preceding EOL instead.
219
+ var ls = fm.contentStart + line.lineStart;
220
+ var le = fm.contentStart + line.lineEnd;
221
+ var cutEnd = le;
222
+ if (text.slice(le, le + 2) === "\r\n") cutEnd = le + 2;
223
+ else if (text.charAt(le) === "\n") cutEnd = le + 1;
224
+ else cutEnd = ls - ((text.charAt(ls - 1) === "\n")
225
+ ? (text.charAt(ls - 2) === "\r" ? 2 : 1) : 0);
226
+ var after = text.slice(0, ls) + text.slice(cutEnd);
227
+ // Empty frontmatter after removal -> strip the whole block.
228
+ // (Cut from the ORIGINAL text: blockEnd indexes into `text`.)
229
+ var restContent = content.slice(0, line.lineStart) + content.slice(line.lineEnd);
230
+ if (restContent.trim() === "") {
231
+ next = text.slice(fm.blockEnd);
232
+ } else {
233
+ next = after;
234
+ }
235
+ nowDisabled = false;
236
+ }
237
+ }
238
+ } else {
239
+ // No frontmatter block.
240
+ if (wantOff) {
241
+ next = "---" + eol + DISABLE_KEY + ": true" + eol + "---" + eol + text;
242
+ nowDisabled = true;
243
+ } else {
244
+ nowDisabled = false; // nothing to enable
245
+ }
246
+ }
247
+
248
+ out.disabled = nowDisabled;
249
+ if (next === null) { out.ok = true; out.changed = false; return out; }
250
+
251
+ // Pre-write backup (byte-exact buffer copy), then write; roll back on failure.
252
+ var bakPath = p + ".bak-toggle-" + backupStamp();
253
+ var original = readFileSync(p); // Buffer
254
+ try {
255
+ writeFileSync(bakPath, original);
256
+ } catch (e2) {
257
+ out.error = "backup failed: " + String(e2 && e2.message || e2);
258
+ return out; // no backup -> no write
259
+ }
260
+ try {
261
+ writeFileSync(p, bom + next, "utf8");
262
+ } catch (e3) {
263
+ try { writeFileSync(p, original); } catch (e4) { /* rollback best-effort */ }
264
+ out.error = "write failed (rolled back): " + String(e3 && e3.message || e3);
265
+ out.backup = bakPath;
266
+ return out;
267
+ }
268
+ out.ok = true;
269
+ out.changed = true;
270
+ out.backup = bakPath;
271
+ return out;
272
+ } catch (e) {
273
+ out.error = String(e && e.message || e);
274
+ return out;
275
+ }
276
+ }
277
+
100
278
  // ── 技能生效/失效台账 ──────────────────────────────
101
279
  // 台账发现(共享版,不限定死路径):
102
280
  // 1) 约定路径优先:<root>/skill-master/references/技能生效-失效台账.md
package/lib/zh.js CHANGED
@@ -3,6 +3,9 @@
3
3
  // 任何技能名清单/字典——既避免泄漏作者技能库构成,也保证所有人看到的都是
4
4
  // 自己技能库的准确描述)。
5
5
  // 分类规则为「目录名前缀」的通用模式匹配,不含任何具体技能名枚举。
6
+ // v1.7.1/1.7.2 rebuild (2026-09-12):新增 UI_TEXT —— 通过 /skills 顶层 ui
7
+ // 字段下发,客户端 T()/TF() 优先取用;键名与 client.js 内置 UI_FALLBACK
8
+ // 完全一致(老宿主/老版本走客户端兜底)。
6
9
 
7
10
  export var ZH = {};
8
11
 
@@ -82,3 +85,57 @@ export var CAT_RULES = [
82
85
  { id: "output", re: /^(concise|caveman|token|new-topic|todo|goal|task-parallel|j-space|conversation-context|chinese-response)(-|$)/ },
83
86
  { id: "fun", re: /^(weather|hatch|pet)(-|$)/ }
84
87
  ];
88
+
89
+ // ── v1.7.1/1.7.2 面板 UI 文案(/skills 顶层 ui 下发)────────────────
90
+ // 键名与 client.js UI_FALLBACK 完全一致;占位符 {name}/{n}/{x}/{y}/{err}
91
+ // 由客户端 TF() 替换,服务端原样下发模板。
92
+ export var UI_TEXT = {
93
+ // v1.7.0 排序/徽章/批量确认(老键保留)
94
+ sortByName: "按名称",
95
+ sortByMtime: "按修改时间",
96
+ sortToggleTitle: "切换排序方式(名称 ↔ 修改时间降序)",
97
+ mtimeLabel: "修改于",
98
+ disabledBadge: "已停用",
99
+ cardDisabledTitle: "已停用(模型技能目录中已摘除)· 点击卡片查看全文",
100
+ toggleEnableTip: "启用后技能恢复进模型技能目录",
101
+ toggleDisableTip: "停用后技能从模型技能目录摘除(SKILL.md 会先自动备份)",
102
+ batchEnable: "全启用",
103
+ batchDisable: "全停用",
104
+ batchBtnTitle: "对该分类下全部技能执行启用/停用",
105
+ batchConfirmDisable: "确定停用该分类下全部技能?(每个 SKILL.md 都会先自动备份,可再次全启用恢复)",
106
+ batchConfirmEnable: "确定启用该分类下全部技能?",
107
+ toggling: "执行中…",
108
+ toggleFailPrefix: "失败",
109
+ toggleDoneSuffix: "完成",
110
+ toggleEnable: "启用",
111
+ toggleDisable: "停用",
112
+ disabledCountSuffix: "项已停用",
113
+ // ── v1.7.1 排序四态 / 滑动开关 / toast ──
114
+ sortNameAsc: "名称↑",
115
+ sortNameDesc: "名称↓",
116
+ sortMtimeDesc: "时间↓",
117
+ sortMtimeAsc: "时间↑",
118
+ sortCycleTitle: "排序(点击循环):名称↑ → 名称↓ → 时间↓(最新在前)→ 时间↑(最旧在前)",
119
+ switchOnText: "开",
120
+ switchOffText: "关",
121
+ toastDisabled: "已停用 {name}",
122
+ toastEnabled: "已启用 {name}",
123
+ toastBatchDisabled: "已停用 {n} 项",
124
+ toastBatchEnabled: "已启用 {n} 项",
125
+ toastBatchPartial: "成功 {x} 项,失败 {y} 项",
126
+ toastToggleFail: "操作失败:{err}",
127
+ processingText: "处理中…",
128
+ // ── v1.7.2 三级滑块开关(全局总开关 / 分类聚合滑块 / 确认菜单)──
129
+ aggAllLabel: "全部技能",
130
+ aggMixedTitle: "部分停用(已停用 {x}/{y})· 点击选择批量启停",
131
+ aggAllOnTitle: "全部启用 · 点击选择批量启停",
132
+ aggAllOffTitle: "全部停用 · 点击选择批量启停",
133
+ aggEnableN: "全部启用 ({n} 项)",
134
+ aggDisableN: "全部停用 ({n} 项)",
135
+ aggDangerText: "将停用全部 {n} 项技能,新会话将不可用这些技能",
136
+ aggMenuTitleGlobal: "全局批量启停 — 作用于技能库全部技能",
137
+ aggMenuTitleCat: "分类批量启停 — 仅作用于当前分类",
138
+ aggMenuStat: "{scope} · 共 {n} 项 · 已停用 {x} 项",
139
+ aggCatStat: "停用 {x}/{y}",
140
+ aggCancelText: "取消"
141
+ };
package/package.json CHANGED
@@ -1,54 +1,59 @@
1
- {
2
- "name": "dsh-skill-browser",
3
- "version": "1.6.2",
4
- "description": "DSH 技能浏览器:悬浮球面板浏览共享技能库(分类/中文简介/搜索/失效台账/全文查看),含 DSH 设置页悬浮球显隐开关与一键恢复默认位置按钮、技能库位置可配置(原生目录选择框)与技能失效台账自动登记(自动/人工分开显示);推荐插件位仅保留面板设置展开区一处。Skill library browser panel with categories, Chinese descriptions, configurable skill root with native directory chooser, one-click ball position reset and automatic skill-failure ledger.",
5
- "type": "module",
6
- "main": "lib/index.js",
7
- "sideEffects": false,
8
- "engines": {
9
- "node": "\u003e=18"
10
- },
11
- "exports": {
12
- ".": "./lib/index.js",
13
- "./client": "./lib/client.js",
14
- "./package.json": "./package.json"
15
- },
16
- "files": [
17
- "lib/**/*.js",
18
- "lib/**/*.ps1",
19
- "cordis.patch.yml",
20
- "README.md",
21
- "LICENSE",
22
- "lib/orb/*.html"
23
- ],
24
- "license": "MIT",
25
- "keywords": [
26
- "dsh-plugin",
27
- "deepseek-harness",
28
- "skill",
29
- "技能",
30
- "skills-browser",
31
- "skill-browser",
32
- "fluent",
33
- "cfd",
34
- "分类",
35
- "category",
36
- "chinese",
37
- "中文简介"
38
- ],
39
- "dsh": {
40
- "bundle": {
41
- "patch": "./cordis.patch.yml"
42
- },
43
- "client": {
44
- "inject": [
45
- "@deepseek-ai/dsh-client-connection",
46
- "@deepseek-ai/dsh-client-runtime",
47
- "@deepseek-ai/dsh-client-ui-settings",
48
- "@deepseek-ai/dsh-client-ui-slots",
49
- "@deepseek-ai/dsh-client-locale"
50
- ],
51
- "platform": "web"
52
- }
53
- }
1
+ {
2
+ "name": "dsh-skill-browser",
3
+ "version": "1.8.1",
4
+ "description": "DSH 技能浏览器:悬浮球面板浏览共享技能库(分类/中文简介/搜索/失效台账/全文查看),含 DSH 设置页悬浮球显隐开关与一键恢复默认位置按钮、技能库位置可配置(原生目录选择框)与技能失效台账自动登记(自动/人工分开显示);推荐插件位仅保留面板设置展开区一处。Skill library browser panel with categories, Chinese descriptions, configurable skill root with native directory chooser, one-click ball position reset and automatic skill-failure ledger.",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "sideEffects": false,
8
+ "engines": {
9
+ "node": "\u003e=18"
10
+ },
11
+ "exports": {
12
+ ".": "./lib/index.js",
13
+ "./client": "./lib/client.js",
14
+ "./package.json": "./package.json"
15
+ },
16
+ "files": [
17
+ "lib/**/*.js",
18
+ "lib/**/*.ps1",
19
+ "cordis.patch.yml",
20
+ "README.md",
21
+ "LICENSE",
22
+ "lib/orb/*.html"
23
+ ],
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/loyalchiiina/dsh-skill-browser.git"
28
+ },
29
+ "homepage": "https://github.com/loyalchiiina/dsh-skill-browser#readme",
30
+ "keywords": [
31
+ "dsh-plugin",
32
+ "deepseek-harness",
33
+ "skill",
34
+ "技能",
35
+ "skills-browser",
36
+ "skill-browser",
37
+ "fluent",
38
+ "cfd",
39
+ "分类",
40
+ "category",
41
+ "chinese",
42
+ "中文简介"
43
+ ],
44
+ "dsh": {
45
+ "bundle": {
46
+ "patch": "./cordis.patch.yml"
47
+ },
48
+ "client": {
49
+ "inject": [
50
+ "@deepseek-ai/dsh-client-connection",
51
+ "@deepseek-ai/dsh-client-runtime",
52
+ "@deepseek-ai/dsh-client-ui-settings",
53
+ "@deepseek-ai/dsh-client-ui-slots",
54
+ "@deepseek-ai/dsh-client-locale"
55
+ ],
56
+ "platform": "web"
57
+ }
58
+ }
54
59
  }