@daweifu/capability-menu 0.1.1 → 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/lib/client.js CHANGED
@@ -5195,10 +5195,11 @@ const capabilityRow$schema = object({
5195
5195
  kind: union([literal("tool"), literal("skill")]).readonly(),
5196
5196
  name: string().readonly(),
5197
5197
  server: string().optional().readonly(),
5198
+ source: string().optional().readonly(),
5198
5199
  class: union([
5199
- literal("exposed"),
5200
- literal("progressive"),
5201
- literal("blocked")
5200
+ literal("resident"),
5201
+ literal("on-demand"),
5202
+ literal("disabled")
5202
5203
  ]).readonly(),
5203
5204
  classLabel: string().optional().readonly(),
5204
5205
  mandatory: boolean().readonly()
@@ -5207,6 +5208,14 @@ const skillFileEntry$schema = object({
5207
5208
  name: string().readonly(),
5208
5209
  type: union([literal("file"), literal("directory")]).readonly()
5209
5210
  });
5211
+ const catalogDocs$schema = object({
5212
+ policyYaml: string().readonly(),
5213
+ catalog: object({
5214
+ path: string().readonly(),
5215
+ content: string().readonly()
5216
+ }).optional().readonly(),
5217
+ catalogMissing: union([literal("disabled"), literal("read-failed")]).optional().readonly()
5218
+ });
5210
5219
  const toolDetail$schema = object({
5211
5220
  id: string().readonly(),
5212
5221
  kind: union([literal("tool"), literal("skill")]).readonly(),
@@ -5219,7 +5228,8 @@ const toolDetail$schema = object({
5219
5228
  origin: object({
5220
5229
  provider: string().readonly(),
5221
5230
  serverName: string().optional().readonly(),
5222
- path: string().optional().readonly()
5231
+ path: string().optional().readonly(),
5232
+ source: string().optional().readonly()
5223
5233
  }).readonly(),
5224
5234
  tags: array(string()).readonly(),
5225
5235
  stats: object({
@@ -5247,7 +5257,7 @@ const TYPERT_REMOTE = {
5247
5257
  },
5248
5258
  sourceLocation: {
5249
5259
  file: "src/server/remote.ts",
5250
- line: 47,
5260
+ line: 60,
5251
5261
  column: 3
5252
5262
  }
5253
5263
  },
@@ -5274,7 +5284,7 @@ const TYPERT_REMOTE = {
5274
5284
  },
5275
5285
  sourceLocation: {
5276
5286
  file: "src/server/remote.ts",
5277
- line: 53,
5287
+ line: 66,
5278
5288
  column: 3
5279
5289
  }
5280
5290
  },
@@ -5292,7 +5302,7 @@ const TYPERT_REMOTE = {
5292
5302
  },
5293
5303
  sourceLocation: {
5294
5304
  file: "src/server/remote.ts",
5295
- line: 59,
5305
+ line: 72,
5296
5306
  column: 3
5297
5307
  }
5298
5308
  },
@@ -5329,7 +5339,7 @@ const TYPERT_REMOTE = {
5329
5339
  },
5330
5340
  sourceLocation: {
5331
5341
  file: "src/server/remote.ts",
5332
- line: 78,
5342
+ line: 84,
5333
5343
  column: 3
5334
5344
  }
5335
5345
  },
@@ -5365,7 +5375,7 @@ const TYPERT_REMOTE = {
5365
5375
  },
5366
5376
  sourceLocation: {
5367
5377
  file: "src/server/remote.ts",
5368
- line: 84,
5378
+ line: 90,
5369
5379
  column: 3
5370
5380
  }
5371
5381
  },
@@ -5392,7 +5402,25 @@ const TYPERT_REMOTE = {
5392
5402
  },
5393
5403
  sourceLocation: {
5394
5404
  file: "src/server/remote.ts",
5395
- line: 70,
5405
+ line: 78,
5406
+ column: 3
5407
+ }
5408
+ },
5409
+ {
5410
+ id: "@daweifu/capability-menu#capabilityPolicy/getCatalogDocs",
5411
+ service: "capabilityPolicy",
5412
+ namespace: "capabilityPolicy",
5413
+ method: "getCatalogDocs",
5414
+ invocation: { kind: "direct" },
5415
+ parameters: [],
5416
+ result: {
5417
+ mode: "strict",
5418
+ typeSymbol: "@daweifu/capability-menu#CatalogDocs",
5419
+ schema: catalogDocs$schema
5420
+ },
5421
+ sourceLocation: {
5422
+ file: "src/server/remote.ts",
5423
+ line: 100,
5396
5424
  column: 3
5397
5425
  }
5398
5426
  }
@@ -5414,29 +5442,39 @@ async function loadSnapshot(remote) {
5414
5442
  /**
5415
5443
  * ⚠️ VERIFIED AGAINST REAL rc.8 CLIENT API.
5416
5444
  *
5417
- * React section for the 能力菜单 settings tab. Follows the real dsh client
5418
- * pattern (see `dsh-client-ui-settings-plugins`): two tabs (MCP 工具 / Skills)
5445
+ * React section for the 能力管理 settings tab. Follows the real dsh client
5446
+ * pattern (see `dsh-client-ui-settings-plugins`): two tabs (工具 / Skills)
5419
5447
  * under one heading, each listing capabilities with a clickable class chip.
5420
5448
  *
5421
5449
  * Layout:
5422
- * - summary strip → per-class counts for the active tab
5423
- * - tabs → MCP tools | Skills (plugins-tab chrome)
5424
- * - MCP tab → grouped by server, collapsible disclosure rows; the
5450
+ * - summary strip → per-class counts for the active tab (Skills tab counts
5451
+ * its active 全局/项目 sub-tab)
5452
+ * - tabs → Tools | Skills (plugins-tab chrome)
5453
+ * - Tools tab → grouped by server, collapsible disclosure rows; the
5425
5454
  * per-class count chip and each tool's class chip are
5426
- * clickable to cycle Progressive → Blocked → Exposed
5427
- * - Skills tab → flat skill rows with the same clickable class chip
5428
- * plus a directory tree / file preview
5455
+ * clickable to cycle Resident → On-demand → Disabled.
5456
+ * Harness-native tools (no real MCP server) share the
5457
+ * reserved `built-in` group, shown as 「系统内置」.
5458
+ * - Skills tab → sub-tabs 全局技能 / 项目技能 (always visible), each
5459
+ * with flat skill rows carrying the same clickable class
5460
+ * chip plus a directory tree / file preview
5429
5461
  */
5430
5462
  const CLASS_KEYS = [
5431
- "exposed",
5432
- "progressive",
5433
- "blocked"
5463
+ "resident",
5464
+ "on-demand",
5465
+ "disabled"
5434
5466
  ];
5435
- /** Click-cycle order: Progressive → Blocked → Exposed → Progressive. */
5467
+ /** Click-cycle order on machine values (displayed as On-demand → Disabled → Resident). */
5436
5468
  const NEXT_CLASS = {
5437
- progressive: "blocked",
5438
- blocked: "exposed",
5439
- exposed: "progressive"
5469
+ "on-demand": "disabled",
5470
+ disabled: "resident",
5471
+ resident: "on-demand"
5472
+ };
5473
+ /** i18n keys for the short display labels, keyed by the machine class value. */
5474
+ const CLASS_SHORT_KEYS = {
5475
+ resident: "residentShort",
5476
+ "on-demand": "onDemandShort",
5477
+ disabled: "disabledShort"
5440
5478
  };
5441
5479
  /** Scoped stylesheet: injected once at module scope, like every official bundle. */
5442
5480
  const CSS_ID = "capability-menu-section-css";
@@ -5445,24 +5483,33 @@ const CSS = `
5445
5483
  .mc-heading{margin:0;font-size:18px;font-weight:600}
5446
5484
  .mc-desc{margin:0;color:var(--dsw-alias-label-tertiary);font-size:13px}
5447
5485
  .mc-summary{display:flex;gap:12px;flex-wrap:wrap;justify-content:flex-end;align-items:center;padding-bottom:8px}
5486
+ .mc-catalog-btn{border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;line-height:20px;padding:0 10px;cursor:pointer;white-space:nowrap}
5487
+ .mc-catalog-btn:hover{border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-interactive-bg-hover)}
5488
+ .mc-catalog-tabs{padding:8px 16px 0}
5489
+ .mc-catalog-path{padding:8px 16px 0;margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);word-break:break-all}
5448
5490
  .mc-chip{display:inline-flex;align-items:center;gap:6px;font-size:12px;line-height:20px;white-space:nowrap}
5449
- /* 三态圆点:常驻=实心、按需=半实心、禁用=空心。
5450
- 色盲友好(蓝-黄轴):冷蓝=常驻、暖琥珀=按需、中性灰=禁用——红绿色盲下三者仍可区分。 */
5451
- .mc-dot{width:10px;height:10px;border-radius:50%;flex:none;box-sizing:border-box}
5452
- .mc-dot--exposed{--mc-dot:#527a9c;background:var(--mc-dot)}
5453
- .mc-dot--progressive{--mc-dot:#a57c33;background:linear-gradient(180deg,var(--mc-dot) 0 50%,transparent 50% 100%);border:1px solid var(--mc-dot)}
5454
- .mc-dot--blocked{--mc-dot:#7e7477;background:transparent;border:1px solid var(--mc-dot)}
5455
- body[data-ds-dark-theme] .mc-dot--exposed{--mc-dot:#96b6d1}
5456
- body[data-ds-dark-theme] .mc-dot--progressive{--mc-dot:#d4b26b}
5457
- body[data-ds-dark-theme] .mc-dot--blocked{--mc-dot:#b8abad}
5491
+ /* 三态圆点:常驻=实心、按需=半实心、禁用=圆环+斜杠(禁行标志)。
5492
+ 形状区分之外仍保留色盲友好(蓝-黄轴):冷蓝=常驻、暖琥珀=按需、中性灰=禁用。
5493
+ 三个状态都用内联 SVG 做 mask 绘制,保证 10px 下也是矢量正圆(避免 CSS
5494
+ border-radius 小尺寸的方圆变形);禁用不用空心圆:在「禁用 · 0」这类计数旁,
5495
+ 空心圆容易被误读成数字 0。 */
5496
+ .mc-dot{position:relative;width:13px;height:13px;border-radius:50%;flex:none;box-sizing:border-box}
5497
+ .mc-dot--resident{--mc-dot:#527a9c;background:var(--mc-dot);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Ccircle cx='6' cy='6' r='5.3' fill='%23000'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Ccircle cx='6' cy='6' r='5.3' fill='%23000'/%3E%3C/svg%3E") center/contain no-repeat}
5498
+ .mc-dot--on-demand{--mc-dot:#a57c33;background:var(--mc-dot);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Ccircle cx='6' cy='6' r='4.6' fill='none' stroke='%23000' stroke-width='1.4'/%3E%3Cpath d='M1.4 6 A4.6 4.6 0 0 1 10.6 6 Z' fill='%23000'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Ccircle cx='6' cy='6' r='4.6' fill='none' stroke='%23000' stroke-width='1.4'/%3E%3Cpath d='M1.4 6 A4.6 4.6 0 0 1 10.6 6 Z' fill='%23000'/%3E%3C/svg%3E") center/contain no-repeat}
5499
+ .mc-dot--disabled{--mc-dot:#7e7477;background:var(--mc-dot);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Ccircle cx='6' cy='6' r='4.6' fill='none' stroke='%23000' stroke-width='1.4'/%3E%3Cline x1='3' y1='9' x2='9' y2='3' stroke='%23000' stroke-width='1.4' stroke-linecap='round'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Ccircle cx='6' cy='6' r='4.6' fill='none' stroke='%23000' stroke-width='1.4'/%3E%3Cline x1='3' y1='9' x2='9' y2='3' stroke='%23000' stroke-width='1.4' stroke-linecap='round'/%3E%3C/svg%3E") center/contain no-repeat}
5500
+ body[data-ds-dark-theme] .mc-dot--resident{--mc-dot:#96b6d1}
5501
+ body[data-ds-dark-theme] .mc-dot--on-demand{--mc-dot:#d4b26b}
5502
+ body[data-ds-dark-theme] .mc-dot--disabled{--mc-dot:#b8abad}
5458
5503
  /* 同上色系(低饱和灰调):仅文字着色,不加背景,浅/深主题各一档。 */
5459
- .mc-chip--exposed{color:#527a9c}
5460
- .mc-chip--progressive{color:#a57c33}
5461
- .mc-chip--blocked{color:#7e7477}
5462
- body[data-ds-dark-theme] .mc-chip--exposed{color:#96b6d1}
5463
- body[data-ds-dark-theme] .mc-chip--progressive{color:#d4b26b}
5464
- body[data-ds-dark-theme] .mc-chip--blocked{color:#b8abad}
5504
+ .mc-chip--resident{color:#527a9c}
5505
+ .mc-chip--on-demand{color:#a57c33}
5506
+ .mc-chip--disabled{color:#7e7477}
5507
+ body[data-ds-dark-theme] .mc-chip--resident{color:#96b6d1}
5508
+ body[data-ds-dark-theme] .mc-chip--on-demand{color:#d4b26b}
5509
+ body[data-ds-dark-theme] .mc-chip--disabled{color:#b8abad}
5465
5510
  .mc-tabs{border-bottom:1px solid var(--dsw-alias-border-l2);display:flex;align-items:flex-end;justify-content:space-between;gap:22px}
5511
+ /* Skills sub-tab bar (全局技能/项目技能): same underline chrome, no right-side summary. */
5512
+ .mc-subtabs{border-bottom:1px solid var(--dsw-alias-border-l2);display:flex;align-items:flex-end;gap:22px}
5466
5513
  .mc-tab-group{display:flex;align-items:flex-end;gap:22px}
5467
5514
  .mc-tab{color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:0;padding:7px 1px 9px;font-size:13px;line-height:20px;position:relative}
5468
5515
  .mc-tab:hover,.mc-tab[data-active=true]{color:var(--dsw-alias-label-primary)}
@@ -5483,12 +5530,12 @@ body[data-ds-dark-theme] .mc-chip--blocked{color:#b8abad}
5483
5530
  .mc-count{font-size:11px;line-height:18px;padding:0 8px;border-radius:999px;border:1px solid transparent;font-family:inherit;cursor:pointer;display:inline-flex;align-items:center;gap:5px;white-space:nowrap}
5484
5531
  .mc-count:hover{border-color:var(--dsw-alias-border-l3)}
5485
5532
  .mc-count:disabled{cursor:default;opacity:.6}
5486
- .mc-count--exposed{color:#527a9c}
5487
- .mc-count--progressive{color:#a57c33}
5488
- .mc-count--blocked{color:#7e7477}
5489
- body[data-ds-dark-theme] .mc-count--exposed{color:#96b6d1}
5490
- body[data-ds-dark-theme] .mc-count--progressive{color:#d4b26b}
5491
- body[data-ds-dark-theme] .mc-count--blocked{color:#b8abad}
5533
+ .mc-count--resident{color:#527a9c}
5534
+ .mc-count--on-demand{color:#a57c33}
5535
+ .mc-count--disabled{color:#7e7477}
5536
+ body[data-ds-dark-theme] .mc-count--resident{color:#96b6d1}
5537
+ body[data-ds-dark-theme] .mc-count--on-demand{color:#d4b26b}
5538
+ body[data-ds-dark-theme] .mc-count--disabled{color:#b8abad}
5492
5539
  .mc-tools{border-top:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base)}
5493
5540
  .mc-tool{display:flex;align-items:center;gap:10px;padding:7px 12px 7px 26px;font-size:13px;line-height:20px;cursor:pointer}
5494
5541
  .mc-tool:hover{background:var(--dsw-alias-interactive-bg-hover)}
@@ -5501,6 +5548,7 @@ body[data-ds-dark-theme] .mc-count--blocked{color:#b8abad}
5501
5548
  .mc-empty{padding:16px;text-align:center;font-size:13px;color:var(--dsw-alias-label-tertiary);border:1px dashed var(--dsw-alias-border-l2);border-radius:8px}
5502
5549
  .mc-error{padding:12px;border:1px solid var(--dsw-alias-state-error-primary);border-radius:8px;color:var(--dsw-alias-state-error-primary);font-size:13px}
5503
5550
  .mc-error pre{margin:8px 0 0;white-space:pre-wrap;word-break:break-all;font-size:11px;color:var(--dsw-alias-label-tertiary)}
5551
+ .mc-notice{padding:10px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-secondary);font-size:13px}
5504
5552
  .mc-skill{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;overflow:hidden;background:var(--dsw-alias-bg-layer-1)}
5505
5553
  .mc-skill-row{box-sizing:border-box;display:flex;align-items:center;gap:10px;width:100%;min-width:0;padding:10px 12px;background:0 0;border:0;color:inherit;font:inherit;text-align:left;cursor:pointer}
5506
5554
  .mc-skill-row:hover{background:var(--dsw-alias-interactive-bg-hover)}
@@ -5520,7 +5568,7 @@ body[data-ds-dark-theme] .mc-count--blocked{color:#b8abad}
5520
5568
  .mc-preview-title{font-size:13px;line-height:20px;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}
5521
5569
  .mc-preview-close{flex:none;padding:2px 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;cursor:pointer}
5522
5570
  .mc-preview-close:hover{background:var(--dsw-alias-interactive-bg-hover)}
5523
- .mc-preview-body{overflow:auto;padding:14px 16px;font-family:var(--dsw-font-markdown-code-block-font-family);font-size:12px;line-height:20px;white-space:pre-wrap;word-break:break-all;color:var(--dsw-alias-label-primary)}
5571
+ .mc-preview-body{overflow:auto;flex:1 1 auto;min-height:0;padding:14px 16px;font-family:var(--dsw-font-markdown-code-block-font-family);font-size:12px;line-height:20px;white-space:pre-wrap;word-break:break-all;color:var(--dsw-alias-label-primary)}
5524
5572
  .mc-preview-hint{padding:16px;text-align:center;font-size:13px;color:var(--dsw-alias-label-tertiary)}
5525
5573
  `;
5526
5574
  if (typeof document !== "undefined" && document.querySelector(`style[data-css-id="${CSS_ID}"]`) === null) {
@@ -5529,6 +5577,10 @@ if (typeof document !== "undefined" && document.querySelector(`style[data-css-id
5529
5577
  tag.textContent = CSS;
5530
5578
  document.head.appendChild(tag);
5531
5579
  }
5580
+ /** The reserved server key grouping harness-native (non-MCP) tools. */
5581
+ const BUILT_IN_SERVER = "built-in";
5582
+ /** Skills whose source root lives inside the current project. */
5583
+ const PROJECT_SOURCES = /* @__PURE__ */ new Set(["project-dsh", "project-agents"]);
5532
5584
  function groupRows(rows) {
5533
5585
  const byServer = /* @__PURE__ */ new Map();
5534
5586
  const skills = [];
@@ -5537,7 +5589,7 @@ function groupRows(rows) {
5537
5589
  skills.push(row);
5538
5590
  continue;
5539
5591
  }
5540
- const server = row.server ?? "builtin";
5592
+ const server = row.server ?? BUILT_IN_SERVER;
5541
5593
  const list = byServer.get(server);
5542
5594
  if (list === void 0) byServer.set(server, [row]);
5543
5595
  else list.push(row);
@@ -5557,6 +5609,7 @@ function CapabilitySection(props) {
5557
5609
  const { remote, t, mountError, remoteKeys } = props;
5558
5610
  const [state, setState] = (0, react.useState)({ status: "loading" });
5559
5611
  const [busy, setBusy] = (0, react.useState)(false);
5612
+ const [notice, setNotice] = (0, react.useState)(null);
5560
5613
  const [openServers, setOpenServers] = (0, react.useState)(/* @__PURE__ */ new Set());
5561
5614
  const [activeTab, setActiveTab] = (0, react.useState)("tools");
5562
5615
  const reload = (0, react.useCallback)(async () => {
@@ -5599,24 +5652,28 @@ function CapabilitySection(props) {
5599
5652
  const key = kind === "skill" ? "skills" : "tools";
5600
5653
  const set = config[key];
5601
5654
  const lists = {
5602
- exposed: [],
5603
- progressive: [],
5604
- blocked: []
5655
+ resident: [],
5656
+ "on-demand": [],
5657
+ disabled: []
5605
5658
  };
5606
5659
  for (const cls of CLASS_KEYS) {
5607
5660
  const raw = set && typeof set === "object" && !Array.isArray(set) ? set[cls] : void 0;
5608
5661
  lists[cls] = Array.isArray(raw) ? raw.filter((x) => typeof x === "string") : [];
5609
5662
  }
5610
- const from = ids.map((id) => state.status === "ready" ? state.snapshot.rows.find((r) => r.id === id)?.class : void 0).find((c) => c !== void 0) ?? "progressive";
5663
+ const from = ids.map((id) => state.status === "ready" ? state.snapshot.rows.find((r) => r.id === id)?.class : void 0).find((c) => c !== void 0) ?? "on-demand";
5611
5664
  const to = NEXT_CLASS[from];
5612
5665
  for (const cls of CLASS_KEYS) lists[cls] = lists[cls].filter((id) => !ids.includes(id));
5613
5666
  lists[to] = [...lists[to], ...ids];
5614
- await remote.updateConfig({ [key]: {
5615
- exposed: lists.exposed,
5616
- progressive: lists.progressive,
5617
- blocked: lists.blocked
5618
- } });
5619
- await reload();
5667
+ const nextLists = {};
5668
+ for (const cls of CLASS_KEYS) nextLists[cls] = lists[cls];
5669
+ await remote.updateConfig({ [key]: nextLists });
5670
+ const next = await loadSnapshot(remote);
5671
+ setState({
5672
+ status: "ready",
5673
+ snapshot: next
5674
+ });
5675
+ const overridden = ids.filter((id) => next.rows.find((r) => r.id === id)?.class !== to);
5676
+ setNotice(overridden.length > 0 ? t("cycleOverridden", { count: overridden.length }) : null);
5620
5677
  } catch (e) {
5621
5678
  setState({
5622
5679
  status: "error",
@@ -5627,9 +5684,9 @@ function CapabilitySection(props) {
5627
5684
  }
5628
5685
  }, [
5629
5686
  remote,
5630
- reload,
5631
5687
  busy,
5632
- state
5688
+ state,
5689
+ t
5633
5690
  ]);
5634
5691
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5635
5692
  className: "mc-section",
@@ -5642,6 +5699,10 @@ function CapabilitySection(props) {
5642
5699
  className: "mc-error",
5643
5700
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: state.message }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { children: `mountError=${String(mountError)}\nremoteKeys=${String(remoteKeys)}` })]
5644
5701
  }),
5702
+ notice !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5703
+ className: "mc-notice",
5704
+ children: notice
5705
+ }),
5645
5706
  state.status === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReadyBody, {
5646
5707
  remote,
5647
5708
  snapshot: state.snapshot,
@@ -5659,7 +5720,10 @@ function CapabilitySection(props) {
5659
5720
  function ReadyBody(props) {
5660
5721
  const { remote, snapshot, openServers, busy, activeTab, t, onTabChange, onToggleServer, onCycle } = props;
5661
5722
  const { servers, skills } = groupRows(snapshot.rows);
5662
- const statRows = activeTab === "tools" ? snapshot.rows.filter((r) => r.kind === "tool") : skills;
5723
+ const projectSkills = skills.filter((skill) => PROJECT_SOURCES.has(skill.source ?? ""));
5724
+ const globalSkills = skills.filter((skill) => !PROJECT_SOURCES.has(skill.source ?? ""));
5725
+ const [skillTab, setSkillTab] = (0, react.useState)("global");
5726
+ const statRows = activeTab === "tools" ? snapshot.rows.filter((r) => r.kind === "tool") : skillTab === "project" ? projectSkills : globalSkills;
5663
5727
  const summary = CLASS_KEYS.map((cls) => ({
5664
5728
  cls,
5665
5729
  count: countByClass(statRows, cls)
@@ -5691,6 +5755,25 @@ function ReadyBody(props) {
5691
5755
  });
5692
5756
  }
5693
5757
  }, [remote, t]);
5758
+ /** 能力目录弹层状态:查看三档策略配置 + 按需能力目录文件。 */
5759
+ const [catalogDocs, setCatalogDocs] = (0, react.useState)(null);
5760
+ const [catalogTab, setCatalogTab] = (0, react.useState)("policy");
5761
+ /** Fetch and show the two read-only catalog documents. */
5762
+ const openCatalogDocs = (0, react.useCallback)(async () => {
5763
+ setCatalogDocs({ status: "loading" });
5764
+ try {
5765
+ const docs = unwrap(await remote.getCatalogDocs(), "capabilityPolicy.getCatalogDocs");
5766
+ setCatalogDocs({
5767
+ status: "ready",
5768
+ docs
5769
+ });
5770
+ } catch (error) {
5771
+ setCatalogDocs({
5772
+ status: "error",
5773
+ message: String(error)
5774
+ });
5775
+ }
5776
+ }, [remote]);
5694
5777
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
5695
5778
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
5696
5779
  className: "mc-heading",
@@ -5723,20 +5806,25 @@ function ReadyBody(props) {
5723
5806
  onClick: () => onTabChange("skills"),
5724
5807
  children: t("skillsGroup")
5725
5808
  })]
5726
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5809
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5727
5810
  className: "mc-summary",
5728
- children: summary.map(({ cls, count }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5811
+ children: [summary.map(({ cls, count }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5729
5812
  className: `mc-chip mc-chip--${cls}`,
5730
5813
  children: [
5731
5814
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5732
5815
  className: `mc-dot mc-dot--${cls}`,
5733
5816
  "aria-hidden": "true"
5734
5817
  }),
5735
- t(`${cls}Short`),
5818
+ t(CLASS_SHORT_KEYS[cls]),
5736
5819
  " · ",
5737
5820
  count
5738
5821
  ]
5739
- }, cls))
5822
+ }, cls)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5823
+ type: "button",
5824
+ className: "mc-catalog-btn",
5825
+ onClick: () => void openCatalogDocs(),
5826
+ children: t("viewCatalog")
5827
+ })]
5740
5828
  })]
5741
5829
  }),
5742
5830
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -5775,7 +5863,7 @@ function ReadyBody(props) {
5775
5863
  }),
5776
5864
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5777
5865
  className: "mc-server-name",
5778
- children: server
5866
+ children: server === BUILT_IN_SERVER ? t("builtInGroup") : server
5779
5867
  }),
5780
5868
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5781
5869
  className: "mc-server-count",
@@ -5799,7 +5887,7 @@ function ReadyBody(props) {
5799
5887
  className: `mc-dot mc-dot--${cls}`,
5800
5888
  "aria-hidden": "true"
5801
5889
  }),
5802
- t(`${cls}Short`),
5890
+ t(CLASS_SHORT_KEYS[cls]),
5803
5891
  " ",
5804
5892
  count
5805
5893
  ]
@@ -5861,13 +5949,49 @@ function ReadyBody(props) {
5861
5949
  children: skills.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5862
5950
  className: "mc-empty",
5863
5951
  children: t("emptySkills")
5864
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
5865
- skills,
5952
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5953
+ className: "mc-subtabs",
5954
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5955
+ className: "mc-tab-group",
5956
+ role: "tablist",
5957
+ "aria-label": t("skillsGroup"),
5958
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5959
+ type: "button",
5960
+ role: "tab",
5961
+ className: "mc-tab",
5962
+ "aria-selected": skillTab === "global",
5963
+ "data-active": skillTab === "global" ? "true" : void 0,
5964
+ onClick: () => setSkillTab("global"),
5965
+ children: t("globalSkills")
5966
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5967
+ type: "button",
5968
+ role: "tab",
5969
+ className: "mc-tab",
5970
+ "aria-selected": skillTab === "project",
5971
+ "data-active": skillTab === "project" ? "true" : void 0,
5972
+ onClick: () => setSkillTab("project"),
5973
+ children: t("projectSkills")
5974
+ })]
5975
+ })
5976
+ }), skillTab === "global" ? globalSkills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
5977
+ skills: globalSkills,
5866
5978
  remote,
5867
5979
  busy,
5868
5980
  t,
5869
5981
  onCycle
5870
- })
5982
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5983
+ className: "mc-empty",
5984
+ children: t("emptyGlobalSkills")
5985
+ }) : projectSkills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
5986
+ skills: projectSkills,
5987
+ remote,
5988
+ busy,
5989
+ t,
5990
+ onCycle
5991
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5992
+ className: "mc-empty",
5993
+ children: t("emptyProjectSkills")
5994
+ })] })
5871
5995
  })
5872
5996
  }),
5873
5997
  toolDetail !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -5910,6 +6034,76 @@ function ReadyBody(props) {
5910
6034
  })
5911
6035
  ]
5912
6036
  })
6037
+ }),
6038
+ catalogDocs !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6039
+ className: "mc-preview-mask",
6040
+ onClick: () => setCatalogDocs(null),
6041
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6042
+ className: "mc-preview",
6043
+ onClick: (e) => e.stopPropagation(),
6044
+ children: [
6045
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6046
+ className: "mc-preview-head",
6047
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6048
+ className: "mc-preview-title",
6049
+ children: t("viewCatalog")
6050
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6051
+ type: "button",
6052
+ className: "mc-preview-close",
6053
+ onClick: () => setCatalogDocs(null),
6054
+ children: t("previewClose")
6055
+ })]
6056
+ }),
6057
+ catalogDocs.status === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6058
+ className: "mc-preview-hint",
6059
+ children: "…"
6060
+ }),
6061
+ catalogDocs.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6062
+ className: "mc-preview-hint",
6063
+ children: catalogDocs.message
6064
+ }),
6065
+ catalogDocs.status === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6066
+ className: "mc-subtabs mc-catalog-tabs",
6067
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6068
+ className: "mc-tab-group",
6069
+ role: "tablist",
6070
+ "aria-label": t("viewCatalog"),
6071
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6072
+ type: "button",
6073
+ role: "tab",
6074
+ className: "mc-tab",
6075
+ "aria-selected": catalogTab === "policy",
6076
+ "data-active": catalogTab === "policy" ? "true" : void 0,
6077
+ onClick: () => setCatalogTab("policy"),
6078
+ children: t("catalogPolicy")
6079
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6080
+ type: "button",
6081
+ role: "tab",
6082
+ className: "mc-tab",
6083
+ "aria-selected": catalogTab === "catalog",
6084
+ "data-active": catalogTab === "catalog" ? "true" : void 0,
6085
+ onClick: () => setCatalogTab("catalog"),
6086
+ children: t("catalogOnDemand")
6087
+ })]
6088
+ })
6089
+ }), catalogTab === "policy" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6090
+ className: "mc-catalog-path",
6091
+ children: t("catalogPolicyNote")
6092
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
6093
+ className: "mc-preview-body",
6094
+ children: catalogDocs.docs.policyYaml
6095
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [catalogDocs.docs.catalog !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6096
+ className: "mc-catalog-path",
6097
+ children: catalogDocs.docs.catalog.path
6098
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6099
+ className: "mc-catalog-path",
6100
+ children: t("catalogOnDemand")
6101
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
6102
+ className: "mc-preview-body",
6103
+ children: catalogDocs.docs.catalog?.content ?? (catalogDocs.docs.catalogMissing === "disabled" ? t("catalogDisabled") : t("catalogUnreadable"))
6104
+ })] })] })
6105
+ ]
6106
+ })
5913
6107
  })
5914
6108
  ] });
5915
6109
  }
@@ -6125,7 +6319,7 @@ function SkillList(props) {
6125
6319
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6126
6320
  className: `mc-dot mc-dot--${skill.class}`,
6127
6321
  "aria-hidden": "true"
6128
- }), t(`${skill.class}Short`)]
6322
+ }), t(CLASS_SHORT_KEYS[skill.class])]
6129
6323
  })]
6130
6324
  })
6131
6325
  ]
@@ -6180,59 +6374,83 @@ const inject = [
6180
6374
  "locale",
6181
6375
  "remote"
6182
6376
  ];
6183
- /** Register the 能力菜单 section once `settings.section` is on the ledger. */
6377
+ /** Register the 能力管理 section once `settings.section` is on the ledger. */
6184
6378
  async function apply(ctx) {
6185
6379
  const zh = {
6186
- nav: "能力菜单",
6187
- title: "能力菜单",
6188
- desc: "管理 MCP / Skill 的 常驻 / 按需 / 禁用 三档分类。",
6189
- exposed: "Exposed(常驻上下文)",
6190
- progressive: "Progressive(按需发现)",
6191
- blocked: "Blocked(禁用)",
6380
+ nav: "能力管理",
6381
+ title: "能力管理",
6382
+ desc: "管理工具与技能的 常驻 / 按需 / 禁用 三档分类。",
6383
+ resident: "Resident(常驻上下文)",
6384
+ "on-demand": "On-demand(按需发现)",
6385
+ disabled: "Disabled(禁用)",
6192
6386
  kind: "类型",
6193
6387
  class: "分类",
6194
6388
  tool: "tool",
6195
6389
  skill: "skill",
6196
6390
  mandatory: "meta",
6197
6391
  rules: "规则",
6198
- toolsGroup: "MCP tools",
6392
+ toolsGroup: "Tools",
6199
6393
  skillsGroup: "Skills",
6200
- emptyTools: "暂无 MCP 工具",
6394
+ builtInGroup: "系统内置",
6395
+ globalSkills: "全局技能",
6396
+ projectSkills: "项目技能",
6397
+ emptyGlobalSkills: "暂无全局技能",
6398
+ emptyProjectSkills: "暂无项目技能",
6399
+ emptyTools: "暂无工具",
6201
6400
  emptySkills: "暂无 Skill",
6202
6401
  toolCount: "{count} 个工具",
6203
- exposedShort: "常驻",
6204
- progressiveShort: "按需",
6205
- blockedShort: "禁用",
6402
+ residentShort: "常驻",
6403
+ onDemandShort: "按需",
6404
+ disabledShort: "禁用",
6206
6405
  cycleHint: "点击标签切换分类(按需 → 禁用 → 常驻)",
6207
6406
  notPreviewable: "该文件不是可预览的文本文件",
6208
6407
  previewClose: "关闭",
6209
- detailNotFound: "未找到该工具的详情"
6408
+ detailNotFound: "未找到该工具的详情",
6409
+ cycleOverridden: "分类未生效:{count} 个能力被更高优先级规则覆盖(如通配规则),可移除对应通配规则后重试",
6410
+ viewCatalog: "查看能力目录",
6411
+ catalogPolicy: "三档策略配置",
6412
+ catalogOnDemand: "按需能力目录",
6413
+ catalogPolicyNote: "实时生成,只读;持久化入口为 cordis.patch.yml(未列出规则的能力默认常驻)",
6414
+ catalogDisabled: "按需能力目录未启用(catalogFile 为空)",
6415
+ catalogUnreadable: "按需能力目录文件读取失败"
6210
6416
  };
6211
6417
  const en = {
6212
- nav: "Capability Menu",
6213
- title: "Capability Menu",
6214
- desc: "Manage the Resident / On-demand / Forbidden classification of MCP tools and skills.",
6215
- exposed: "Exposed (context-resident)",
6216
- progressive: "Progressive (on-demand)",
6217
- blocked: "Blocked (forbidden)",
6418
+ nav: "Capability Management",
6419
+ title: "Capability Management",
6420
+ desc: "Manage the Resident / On-demand / Disabled classification of tools and skills.",
6421
+ resident: "Resident",
6422
+ "on-demand": "On-demand",
6423
+ disabled: "Disabled",
6218
6424
  kind: "kind",
6219
6425
  class: "class",
6220
6426
  tool: "tool",
6221
6427
  skill: "skill",
6222
6428
  mandatory: "meta",
6223
6429
  rules: "rules",
6224
- toolsGroup: "MCP tools",
6430
+ toolsGroup: "Tools",
6225
6431
  skillsGroup: "Skills",
6226
- emptyTools: "No MCP tools",
6432
+ builtInGroup: "System built-in",
6433
+ globalSkills: "Global skills",
6434
+ projectSkills: "Project skills",
6435
+ emptyGlobalSkills: "No global skills",
6436
+ emptyProjectSkills: "No project skills",
6437
+ emptyTools: "No tools",
6227
6438
  emptySkills: "No skills",
6228
6439
  toolCount: "{count} tools",
6229
- exposedShort: "Resident",
6230
- progressiveShort: "On-demand",
6231
- blockedShort: "Forbidden",
6232
- cycleHint: "Click a tag to cycle its classification (On-demand → Forbidden → Resident)",
6440
+ residentShort: "Resident",
6441
+ onDemandShort: "On-demand",
6442
+ disabledShort: "Disabled",
6443
+ cycleHint: "Click a tag to cycle its classification (On-demand → Disabled → Resident)",
6233
6444
  notPreviewable: "This file is not a previewable text file",
6234
6445
  previewClose: "Close",
6235
- detailNotFound: "Tool detail not found"
6446
+ detailNotFound: "Tool detail not found",
6447
+ cycleOverridden: "Classification not applied: {count} capability(ies) overridden by a higher-priority rule (e.g. a wildcard). Remove the matching wildcard rule and retry.",
6448
+ viewCatalog: "View capability catalog",
6449
+ catalogPolicy: "Policy (effective)",
6450
+ catalogOnDemand: "On-demand catalog",
6451
+ catalogPolicyNote: "Generated live from the effective policy (read-only); persist via cordis.patch.yml. Capabilities without a rule default to Resident.",
6452
+ catalogDisabled: "On-demand catalog emission is disabled (catalogFile is empty).",
6453
+ catalogUnreadable: "Failed to read the on-demand catalog file."
6236
6454
  };
6237
6455
  ctx.effect(() => ctx.locale.register(NS, {
6238
6456
  zh,