@yottameta/yotta-skills-plugin 0.28.2 → 0.28.3

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.
@@ -12,7 +12,7 @@
12
12
  "authentication": "ON_INSTALL"
13
13
  },
14
14
  "category": "Developer Tools",
15
- "version": "0.28.2",
15
+ "version": "0.28.3",
16
16
  "description": "YuanGe (元阁) - orchestration routing, inventory, and one-command installer for the YottaMeta skill family (zero dependencies, Node.js only).",
17
17
  "interface": {
18
18
  "displayName": "元阁 yotta-skills",
@@ -12,7 +12,7 @@
12
12
  "authentication": "ON_INSTALL"
13
13
  },
14
14
  "category": "Developer Tools",
15
- "version": "0.28.2",
15
+ "version": "0.28.3",
16
16
  "description": "YuanGe (元阁) - orchestration routing, inventory, and one-command installer for the YottaMeta skill family (zero dependencies, Node.js only).",
17
17
  "interface": {
18
18
  "displayName": "元阁 yotta-skills",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yottameta/yotta-skills-plugin",
3
- "version": "0.28.2",
3
+ "version": "0.28.3",
4
4
  "description": "YuanGe (元阁) — orchestration and routing for the YottaMeta skill family, packaged as a YottaMeta Agent Plugin and DeepSeek Harness profile bundle.",
5
5
  "license": "MIT",
6
6
  "keywords": [
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "yotta-skills",
4
- "version": "0.28.2",
4
+ "version": "0.28.3",
5
5
  "description": "YuanGe (元阁) - orchestration routing, inventory, and one-command installer for the YottaMeta skill family (zero dependencies, Node.js only).",
6
6
  "author": {
7
7
  "name": "YottaMeta",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: yotta-skills
3
- version: 0.28.2
3
+ version: 0.28.3
4
4
  description: 元阁 -- 元阁全家技能的总编排策划 + 编排路由 + 一键安装器 + 技能盘点 + 本机技能 Hub + 运行时 hook 适配。Hub 层:hub hosts 只读发现本机已装智能体与技能目录(文件系统优先,不读元忆;按已核实 / 自动发现 / 桥接分类);hub install / update 把技能装到 ~/.yottaskills/hub 单点真源(清单 27 + 特殊家族 5,特殊家族跟随各自 npm latest);hub link --all 用 Windows junction / POSIX symlink 分发到全部已核实宿主(默认范围;--include-discovered 显式纳入自动发现目录;锁 / 数据桥接目录永不链接),元技能在链接时收敛宿主旧副本(移入 ~/.yottaskills/trash 保留 7 天、版本闸门 Hub ≥ 宿主);hub unlink 只删链接、fail-closed;非元技能不参与更新(用户自行处理);hub status 显示来源、版本、链接与异常;view 启动本机技能枢纽面板(默认 127.0.0.1:8789,六视图 + 高级 CLI 页,写操作需页面令牌与确认);兼容 agentskills.io 技能格式、Vercel Labs skills CLI 的 .agents/skills 通用目录与 .skill-lock.json v3(只读)。路由层:--route / route_request 按需求摘要给出候选组合、调用顺序、角色、置信度、依据、已装/缺失状态与安装命令,只建议不自动安装;非元阁家族已装技能按 frontmatter description 机械匹配作并列候选(标注来源与未扫描状态,只读不自动调用);M1 记忆裁决层:usage enable/mark 本地结构化记录 + decide-memory / MCP decide_memory 输出 promote / hold / demote 只读建议(需授权 provider;只建议不删除、不自动写元忆);策划层:按场景给出「该组合哪几个元技能、组合强在哪、怎么组合使用(安装与调用均由用户确认后执行)」;安装层:一条命令把 YottaMeta 已发布的全部 yotta-* 技能装进指定智能体或目录(默认 --pin 锁死清单精确版本);盘点层:--inventory / --reindex 扫描本机已装技能生成/更新注册表,新装技能自动被发现(install/update 后自动 re-index,会话开工只建议跑本地 --reindex;更新检查走手动 --check 或后台 --check --scheduled);运行时适配层:hook capabilities / evaluate / bind / unbind 按宿主能力矩阵执行六个统一事件并留证降级,元信 before_install 已接入安装管线;自包含零依赖,不依赖任何元技能;MCP 按需加载且需用户确认后写入配置(可选:list_installed_skills/describe_skill/reindex/route_request/decide_memory,不常驻,未加载降级 CLI)。支持 --list 清单 / --route 路由 / usage 使用记录 / decide-memory 记忆裁决 / hub 技能 Hub / install / update / update --check(只读检查)/ update --check --scheduled(后台周检)/ update --auto(家族自动更新)/ hook 适配 / --inventory / --reindex / --dry-run 预览 / --pin(默认)/ --range。触发:需要批量安装或更新元阁全家技能、单点安装并分发到多个智能体、查看本机装了哪些智能体与技能目录、接管非元技能、按场景组合多个元技能、路由或判断该用哪些技能、判断哪些技能值得长期记忆、查看或记录技能使用信号、盘点或查看本机已装技能、重扫技能注册表、给某个智能体或目录一次性铺齐 yotta-* 技能、评估宿主 hook 能力、预览安装清单、锁版本安装、或用户说 元阁/装全家/一次装齐/yotta-skills/install-all/更新全家/检查更新/自动更新/hook 适配/该用哪个技能/路由技能/记忆裁决/技能该不该记住/盘点技能/查看已装技能/技能 Hub/单点安装/链接分发/接管技能/技能枢纽面板/图形化面板 等。边界(Do NOT trigger):只做「组合策划 + 静态路由建议 + M1 记忆裁决只读建议 + 清单 + 下载 + 落位 + 汇总 + 盘点 + re-index + Hub 本机真源与链接分发 + hook 适配」,不含技能本体、不做技能内容开发、不 -g 污染全局、不自动安装缺失技能、不静默写宿主配置或全局记忆、不自动删除技能或记忆;家族安装先自举或调用元信装前门禁,DO NOT INSTALL 阻断,非元阁家族包不自动安装。
5
5
  license: MIT
6
6
  metadata:
@@ -193,6 +193,13 @@ npx -y @yottameta/yotta-skills view
193
193
 
194
194
  # 只删链接,不动 Hub 真源(真目录 / 外部链接一律拒绝)
195
195
  npx -y @yottameta/yotta-skills hub unlink --all
196
+
197
+ # 指定智能体分发 / 解除(只影响该宿主;Hub 真源保留)
198
+ npx -y @yottameta/yotta-skills hub link yotta-memory --agent codex
199
+ npx -y @yottameta/yotta-skills hub unlink yotta-memory --agent codex
200
+
201
+ # 彻底删除 Hub 技能:全宿主清链接(含死链)+ 目录入回收站 + 清台账 + 审计
202
+ npx -y @yottameta/yotta-skills hub remove my-skill --dry-run
196
203
  ```
197
204
 
198
205
  链接范围(0.28.0 起):`hub hosts` 把目录分为已核实 / 自动发现 / 桥接三类 ——
@@ -207,6 +214,13 @@ npx -y @yottameta/yotta-skills hub unlink --all
207
214
  `.skill-lock.json` v3(`$XDG_STATE_HOME/skills/` 或 `~/.agents/`)。
208
215
  不读元忆,不重写官方锁文件,不静默降级为复制。
209
216
 
217
+ 删除技能(0.28.3 起):`hub remove <slug>` 一条命令完成「全宿主清链接(含死链)→
218
+ Hub 目录入回收站(保留 7 天)→ 清 Hub / 链接台账 → 写审计」,fail-closed:只删指向
219
+ Hub 的链接与 Hub 内目录,真副本 / 外部链接一律保留并报告;链接清理报错即中止、不删
220
+ Hub。默认范围为已核实宿主 ∪ 本技能链接台账;`--agent` / `--dir` 收窄仅用于 Hub 目标
221
+ 已缺失的死链清理(防止只清一个宿主导致其他宿主变死链)。指定智能体分发 / 解除用
222
+ `hub link|unlink <slug> --agent <id>`。
223
+
210
224
  ## 命令与选项
211
225
 
212
226
  | 命令 / 选项 | 作用 |
@@ -235,9 +249,10 @@ npx -y @yottameta/yotta-skills hub unlink --all
235
249
  | `hub refresh <slug> --from <path>` | 手动同步非元阁技能(重新收编 + 更新哈希 + 台账) |
236
250
  | `hub link --agent <id> \| --dir <dir> \| --all [--include-discovered]` | 把 Hub 技能链接到指定宿主 / 目录 / 全部已核实宿主(默认范围);`--include-discovered` 显式纳入自动发现目录;元技能链接时收敛旧副本(回收站 7 天);`--dry-run` 只预览(含将收敛明细);`--force` 可替换指向 Hub 之外的链接(仅移除链接本身) |
237
251
  | `hub unlink --agent <id> \| --dir <dir> \| --all [--include-discovered]` | 只删除链接;lstat + readlink 校验目标在 Hub 内,fail-closed;清理范围外链接用 `--dir <目录>` 精确指定 |
252
+ | `hub remove <slug> [--dry-run] [--include-discovered]` | 删除 Hub 技能:全宿主清链接(含死链)+ Hub 目录入回收站(7 天)+ 清 Hub / 链接台账 + 写审计;fail-closed(真副本 / 外部链接保留并报告;链接报错即中止不删 Hub);`--agent` / `--dir` 收窄仅用于死链清理 |
238
253
  | `hub status [--json]` | 查看 Hub 技能来源、版本、链接宿主、异常链接与宿主发现摘要 |
239
254
  | `hub doctor [--json]` | 检查断链 / Hub 目标缺失 / slug 不一致 / 单一真源(只读报告多份副本与版本参差)/ 链接范围(`link_scope` 只读报告范围外链接)/ 目录权限;异常时退出码 1 |
240
- | `view [--port <n>]` | 启动本机技能枢纽面板(默认 `127.0.0.1:8789`,仅本机监听):六视图(概览 / 宿主矩阵 / 收编向导 / 链接与体检 / 记录与回滚 / 路由与编排)+ 高级 CLI 页;可执行收编 / 链接 / 解除 / 回滚,写操作需页面会话令牌与破坏性确认,install / update / refresh 只给命令 |
255
+ | `view [--port <n>]` | 启动本机技能枢纽面板(默认 `127.0.0.1:8789`,仅本机监听):六视图(概览 / 宿主矩阵 / 收编向导 / 链接与体检 / 记录与回滚 / 路由与编排)+ **全量 CLI 页**(全部命令 / 子命令 + 人话说明 + 可复制示例 + 完整选项,与 `--help` 单一真源);可执行收编 / 链接 / 解除 / 回滚 / 删除技能(影响预览 + 手输 slug 二次确认),写操作需页面会话令牌,install / update / refresh 只给命令 |
241
256
  | `--route <需求摘要>` | 编排路由:输出组合、调用顺序、技能角色、置信度、依据、已装/缺失状态与安装建议;可选本地 provider 增补 / 重排并返回 `confidence` / `reasons` / `summary` / `alternatives`;`--json` 输出 JSON、`--project` 附扫项目级目录 |
242
257
  | `usage status` | 查看本地使用记录开关与计数;默认关闭,不创建文件 |
243
258
  | `usage enable` / `usage disable` | 开启 / 关闭 `--route` 的结构化使用记录;不记录需求原文 |
@@ -132,6 +132,10 @@
132
132
  .cmd code{flex:1;min-width:0;word-break:break-all;color:var(--text)}
133
133
  .cmd .why{color:var(--faint);font-size:11px}
134
134
  .note{color:var(--muted);font-size:12px;margin-top:8px}
135
+ .cmd-detail{border:1px solid var(--line);border-radius:6px;background:var(--panel2);margin-top:8px;padding:0 11px;min-width:0}
136
+ .cmd-detail summary{cursor:pointer;padding:9px 0;display:flex;gap:8px;align-items:baseline;flex-wrap:wrap}
137
+ .cmd-detail .note{padding:0 0 8px}
138
+ .cmd-detail .cmd{margin:0 0 8px}
135
139
 
136
140
  @media (max-width:980px){.g4{grid-template-columns:repeat(2,minmax(0,1fr))}}
137
141
  @media (max-width:760px){
@@ -188,7 +192,10 @@
188
192
  <div class="grid g4" id="ovCards"></div>
189
193
  <div class="section" id="ovDoctor"></div>
190
194
  <div class="section grid g2">
191
- <div class="panel"><h3>Hub 内容</h3><div class="faint">技能真源在 Hub 目录,宿主目录通过链接分发。</div><div class="list" id="ovSkills"></div></div>
195
+ <div class="panel"><h3>Hub 内容</h3><div class="faint">技能真源在 Hub 目录,宿主目录通过链接分发。</div>
196
+ <label class="search section" style="min-width:100%"><svg class="sv" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg><input id="ovSkillFilter" placeholder="过滤技能(输入后显示全部匹配)"></label>
197
+ <div class="list" id="ovSkills"></div>
198
+ </div>
192
199
  <div class="panel"><h3>最近动作</h3><div class="faint">收编 / 链接 / 解除都写入 Hub 审计。</div><div class="list" id="ovAudit"></div></div>
193
200
  </div>
194
201
  <div class="section panel">
@@ -286,9 +293,18 @@
286
293
  <section class="view" id="view-cli">
287
294
  <div class="banner">
288
295
  <svg class="sv" viewBox="0 0 24 24"><path d="m4 7 5 5-5 5"/><path d="M12 19h8"/></svg>
289
- <div><div class="banner-title">高级 CLI</div><div class="banner-body">低频 / 长尾命令在这里复制到终端执行;网页不代替终端运行安装与更新。</div></div>
296
+ <div><div class="banner-title">高级 CLI(全量)</div><div class="banner-body">全部命令 + 人话说明 + 可复制示例;网页只展示与复制,不代替终端执行安装、更新与删除。</div></div>
297
+ </div>
298
+ <div class="section panel">
299
+ <div class="shead"><h2>命令速查</h2><span class="faint" id="cliMeta">加载中…</span></div>
300
+ <div id="cliQuick"></div>
301
+ </div>
302
+ <div class="section panel">
303
+ <details id="cliFullWrap">
304
+ <summary>完整命令与参数(点击展开)</summary>
305
+ <div id="cliFull"></div>
306
+ </details>
290
307
  </div>
291
- <div id="cliGroups"></div>
292
308
  </section>
293
309
  </main>
294
310
  </div>
@@ -299,7 +315,7 @@
299
315
  <script>
300
316
  (function () {
301
317
  var TOKEN = (document.querySelector('meta[name="yotta-view-token"]') || {}).content || '';
302
- var state = { view: 'overview', overview: null, hosts: null, adopt: null, selected: {}, records: null, tab: 'snapshots', hostFilter: '', adoptFilter: '' };
318
+ var state = { view: 'overview', overview: null, hosts: null, adopt: null, selected: {}, records: null, tab: 'snapshots', hostFilter: '', adoptFilter: '', ovSkillFilter: '' };
303
319
 
304
320
  function $(sel) { return document.querySelector(sel); }
305
321
  function esc(value) {
@@ -380,7 +396,7 @@
380
396
  links: { title: '链接与体检', sub: '断链 / 漂移 / 目标缺失在哪、怎么修', load: loadLinks },
381
397
  records: { title: '记录与回滚', sub: '安装、收编、链接都做了什么,怎么退回', load: loadRecords },
382
398
  route: { title: '路由与编排', sub: '一句话需求该用哪些技能、缺什么(只读)', load: null },
383
- cli: { title: '高级 CLI', sub: '低频命令速查与复制', load: null }
399
+ cli: { title: '高级 CLI', sub: '全量命令速查与复制(与 CLI --help 同源)', load: null }
384
400
  };
385
401
  var loadSeq = 0;
386
402
  function showLoad(text) {
@@ -450,10 +466,7 @@
450
466
  statCard('异常链接', d.status.summary.brokenLinks, d.status.summary.brokenLinks ? '需要处理' : '全部正常', d.status.summary.brokenLinks ? 'danger' : 'ok') +
451
467
  statCard('Doctor', d.doctor.ok ? '通过' : '未通过', '错误 ' + d.doctor.summary.errors + ' / 警告 ' + d.doctor.summary.warnings, d.doctor.ok ? 'ok' : 'danger');
452
468
  $('#ovDoctor').innerHTML = doctorBanner(d.doctor);
453
- var skills = d.status.skills || [];
454
- $('#ovSkills').innerHTML = skills.length ? skills.slice(0, 8).map(function (s) {
455
- return '<div class="row"><div class="row-main"><div class="row-title">' + esc(s.slug) + '</div><div class="row-meta">v' + esc(s.version || '-') + ' · ' + esc(s.origin === 'external' ? '外部来源' : '元阁家族') + ' · 链接 ' + (s.links || []).length + '</div></div><span class="badge ' + (s.status === 'present' ? 'ok' : 'danger') + '">' + esc(s.status === 'present' ? '已入库' : '缺失') + '</span></div>';
456
- }).join('') : '<div class="empty">Hub 还是空的。先在「收编向导」把已装技能收编进来。</div>';
469
+ renderOverviewSkills();
457
470
  var audit = (d.audit && d.audit.recent) || [];
458
471
  $('#ovAudit').innerHTML = audit.length ? audit.slice().reverse().map(function (a) {
459
472
  return '<div class="row"><div class="row-main"><div class="row-title">' + esc(a.event || '动作') + ' · ' + esc(a.slug || '') + '</div><div class="row-meta">' + esc(fmtTime(a.at)) + (a.verdict ? ' · ' + esc(a.verdict) : '') + '</div></div></div>';
@@ -462,6 +475,53 @@
462
475
  $('#copyHubDir').onclick = function () { copyText(d.hubDir); };
463
476
  }
464
477
 
478
+ function renderOverviewSkills() {
479
+ var d = state.overview;
480
+ if (!d) return;
481
+ var skills = (d.status && d.status.skills) || [];
482
+ var filter = state.ovSkillFilter.trim().toLowerCase();
483
+ var list = filter
484
+ ? skills.filter(function (s) { return String(s.slug || '').toLowerCase().indexOf(filter) >= 0; })
485
+ : skills.slice(0, 8);
486
+ $('#ovSkills').innerHTML = list.length ? list.map(function (s) {
487
+ return '<div class="row"><div class="row-main"><div class="row-title">' + esc(s.slug) + '</div><div class="row-meta">v' + esc(s.version || '-') + ' · ' + esc(s.origin === 'external' ? '外部来源' : '元阁家族') + ' · 链接 ' + (s.links || []).length + '</div></div>' +
488
+ '<span class="badge ' + (s.status === 'present' ? 'ok' : 'danger') + '">' + esc(s.status === 'present' ? '已入库' : '缺失') + '</span>' +
489
+ '<div class="row-actions"><button class="btn small danger" data-remove-skill="' + esc(s.slug) + '">删除</button></div></div>';
490
+ }).join('') : '<div class="empty">' + (filter ? '没有匹配的技能。' : 'Hub 还是空的。先在「收编向导」把已装技能收编进来。') + '</div>';
491
+ }
492
+ $('#ovSkillFilter').addEventListener('input', function () {
493
+ state.ovSkillFilter = $('#ovSkillFilter').value || '';
494
+ renderOverviewSkills();
495
+ });
496
+ $('#ovSkills').addEventListener('click', async function (e) {
497
+ var b = e.target.closest('[data-remove-skill]');
498
+ if (!b) return;
499
+ var slug = b.dataset.removeSkill;
500
+ var plan = await api('/api/skills/remove-plan?slug=' + encodeURIComponent(slug));
501
+ if (!plan || plan.error) { toast((plan && plan.error) || '影响预览失败'); return; }
502
+ var targets = plan.targets || [];
503
+ var willUnlink = targets.filter(function (t) { return t.action === 'unlink'; });
504
+ var kept = targets.filter(function (t) { return t.action === 'keep'; });
505
+ var rows = targets.map(function (t) {
506
+ var tone = t.action === 'unlink' ? 'warn' : t.action === 'keep' ? 'info' : '';
507
+ return '<div class="result"><span class="badge ' + tone + '">' + esc(t.action === 'unlink' ? '将清理' : t.action === 'keep' ? '保留' : '无动作') + '</span><span class="mono">' + esc(t.dir) + '</span><span class="faint">' + esc(t.note || '') + '</span></div>';
508
+ }).join('');
509
+ var body = '<p class="label">将清理 ' + willUnlink.length + ' 条宿主链接(含死链)' +
510
+ (plan.hubPresent ? ',并把 Hub 目录移入回收站(保留 7 天)' : ';Hub 目录已缺失,仅清理残留') +
511
+ ';保留 ' + kept.length + ' 项宿主真副本 / 外部链接。</p>' +
512
+ '<div class="result-list">' + (rows || '<div class="faint">没有需要处理的宿主目录。</div>') + '</div>' +
513
+ '<div class="section"><label class="label">输入完整技能名确认:' + esc(slug) + '</label><input id="removeConfirmSlug" placeholder="' + esc(slug) + '"></div>';
514
+ confirmModal('删除 Hub 技能 ' + slug + '?', body, '删除', true, async function () {
515
+ var typed = ($('#removeConfirmSlug') && $('#removeConfirmSlug').value || '').trim();
516
+ if (typed !== slug) { toast('技能名不匹配'); return; }
517
+ var r = await api('/api/skills/remove', { body: { slug: slug, confirm: 'remove', confirmSlug: typed } });
518
+ if (r.error) { toast(r.error); return; }
519
+ closeModal();
520
+ toast(r.verdict === 'cleanup-only' ? '已清理残留(Hub 目录原本缺失)' : '已删除 ' + slug + '(目录已入回收站)');
521
+ loadOverview();
522
+ });
523
+ });
524
+
465
525
  async function loadHosts() {
466
526
  var d = await api('/api/hosts');
467
527
  if (d.error) { toast(d.error); return; }
@@ -722,40 +782,56 @@
722
782
  if (b) copyText(b.dataset.copyCmd);
723
783
  });
724
784
 
725
- var CLI_GROUPS = [
726
- { name: 'Hub 发现与安装', items: [
727
- { cmd: 'yotta-skills hub hosts', why: '发现本机宿主与技能目录' },
728
- { cmd: 'yotta-skills hub install', why: '把元阁家族装进 Hub 真源' },
729
- { cmd: 'yotta-skills hub adopt --scan', why: '只读预演收编候选与冲突' },
730
- { cmd: 'yotta-skills hub adopt --apply', why: '执行收编(默认复制保真)' },
731
- ] },
732
- { name: '链接与体检', items: [
733
- { cmd: 'yotta-skills hub link --all', why: '链接到已核实宿主(默认范围)' },
734
- { cmd: 'yotta-skills hub link --all --include-discovered', why: '显式纳入自动发现目录' },
735
- { cmd: 'yotta-skills hub link --agent codex', why: '只链接到一个宿主' },
736
- { cmd: 'yotta-skills hub unlink --all', why: '只删链接,不动 Hub 真源' },
737
- { cmd: 'yotta-skills hub status', why: '查看技能 / 来源 / 链接' },
738
- { cmd: 'yotta-skills hub doctor', why: '检查断链 / 缺失 / slug 不一致' },
739
- ] },
740
- { name: '安装与更新', items: [
741
- { cmd: 'yotta-skills update --installed-only --dir <技能目录>', why: '只维护已装元技能' },
742
- { cmd: 'yotta-skills update --check', why: '只读检查更新' },
743
- { cmd: 'yotta-skills rollback --list', why: '查看可回滚快照' },
744
- { cmd: 'yotta-skills rollback --slug <技能> --dir <技能目录>', why: '回滚单个技能' },
745
- ] },
746
- { name: '路由与使用', items: [
747
- { cmd: 'yotta-skills --route "帮我做发布前质量检查"', why: '静态编排路由 + 动态路由块' },
748
- { cmd: 'yotta-skills --list', why: '列出全家技能与版本' },
749
- { cmd: 'yotta-skills --inventory --json', why: '盘点本机已装技能' },
750
- ] },
751
- ];
752
- $('#cliGroups').innerHTML = CLI_GROUPS.map(function (g) {
753
- return '<div class="panel cmd-group"><h3>' + esc(g.name) + '</h3>' + g.items.map(function (item) {
754
- return '<div class="cmd"><code>' + esc(item.cmd) + '</code><span class="why">' + esc(item.why) + '</span><button class="btn small" data-copy-cmd="' + esc(item.cmd) + '">复制</button></div>';
785
+ /* 高级 CLI:/api/help 单一真源(与 CLI --help 同源) */
786
+ function findHelpEntry(groups, ref) {
787
+ var seg = String(ref).split(' ');
788
+ for (var i = 0; i < groups.length; i++) {
789
+ var cmds = groups[i].commands || [];
790
+ for (var j = 0; j < cmds.length; j++) {
791
+ var c = cmds[j];
792
+ if (c.name !== seg[0]) continue;
793
+ if (seg.length === 1) return c;
794
+ var subs = c.subcommands || [];
795
+ for (var k = 0; k < subs.length; k++) if (subs[k].name === seg[1]) return subs[k];
796
+ }
797
+ }
798
+ return null;
799
+ }
800
+ function renderCliHelp(d) {
801
+ if (!d || d.error) { $('#cliMeta').textContent = '帮助加载失败'; return; }
802
+ var total = 0;
803
+ (d.groups || []).forEach(function (g) { total += (g.commands || []).length; });
804
+ $('#cliMeta').textContent = 'v' + (d.version || '-') + ' · 命令 ' + total + ' · 选项 ' + (d.options || []).length;
805
+ $('#cliQuick').innerHTML = (d.quick || []).map(function (g) {
806
+ return '<div class="cmd-group"><h3>' + esc(g.group) + '</h3>' + (g.items || []).map(function (item) {
807
+ var meta = findHelpEntry(d.groups, item.cmd) || {};
808
+ return '<div class="cmd"><code>' + esc(item.example) + '</code><span class="why">' + esc(meta.what || '') +
809
+ (item.note ? ' · ' + esc(item.note) : '') + '</span>' +
810
+ (item.danger ? '<span class="badge danger">危险</span>' : '') +
811
+ '<button class="btn small" data-copy-cmd="' + esc(item.example) + '">复制</button></div>';
812
+ }).join('') + '</div>';
813
+ }).join('');
814
+ var full = (d.groups || []).map(function (g) {
815
+ return '<div class="cmd-group"><h3>' + esc(g.group) + '</h3>' + (g.commands || []).map(function (c) {
816
+ var opts = (c.options || []).length
817
+ ? '<div class="note">常用选项: ' + c.options.map(function (f) { return '<code>' + esc(f) + '</code>'; }).join(' ') + '</div>'
818
+ : '';
819
+ var subs = (c.subcommands || []).map(function (s) {
820
+ return '<div class="cmd"><code>' + esc('yotta-skills ' + s.usage) + '</code><span class="why">' + esc(s.what || '') + '</span><button class="btn small" data-copy-cmd="' + esc('yotta-skills ' + s.usage) + '">复制</button></div>';
821
+ }).join('');
822
+ return '<details class="cmd-detail"><summary><code>' + esc('yotta-skills ' + c.usage) + '</code> <span class="why">' + esc(c.what || '') + '</span></summary>' +
823
+ (c.when ? '<div class="note">' + esc(c.when) + '</div>' : '') + opts + subs + '</details>';
824
+ }).join('') + '</div>';
825
+ }).join('');
826
+ full += '<div class="cmd-group"><h3>完整选项</h3>' + (d.options || []).map(function (o) {
827
+ return '<div class="cmd"><code>' + esc(o.flag + (o.arg ? ' ' + o.arg : '')) + '</code><span class="why">' + esc(o.what || '') +
828
+ (o.when ? ' · ' + esc(o.when) : '') + (o.caution ? ' · ⚠ ' + esc(o.caution) : '') + '</span></div>';
755
829
  }).join('') + '</div>';
756
- }).join('');
830
+ $('#cliFull').innerHTML = full;
831
+ }
832
+ api('/api/help').then(renderCliHelp);
757
833
  document.addEventListener('click', function (e) {
758
- var b = e.target.closest('#cliGroups [data-copy-cmd]');
834
+ var b = e.target.closest('#view-cli [data-copy-cmd]');
759
835
  if (b) copyText(b.dataset.copyCmd);
760
836
  });
761
837