@yottameta/yotta-skills-plugin 0.29.10 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,7 @@
12
12
  "authentication": "ON_INSTALL"
13
13
  },
14
14
  "category": "Developer Tools",
15
- "version": "0.29.10",
15
+ "version": "0.30.0",
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.29.10",
15
+ "version": "0.30.0",
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.29.10",
3
+ "version": "0.30.0",
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.29.10",
4
+ "version": "0.30.0",
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,7 +1,7 @@
1
1
  ---
2
2
  name: yotta-skills
3
- version: 0.29.10
4
- description: 元阁 -- 元阁全家技能的总编排策划 + 编排路由 + 一键安装器 + 技能盘点 + 本机技能 Hub + 运行时 hook 适配。Hub 层:hub hosts 只读发现本机已装智能体与技能目录(文件系统优先,不读元忆;已核实 / 自动发现 / 桥接分类 + 状态细分:可用 / 残留(实体未确认)/ 未创建 / 仅标记);hub hosts add/remove/list/mark/exclude/include/set 注册自定义宿主目录、手动标记、不接管开关与目录覆盖(只注册不建目录、移除不删目录;残留清理 hub hosts remove --purge 默认预览、目录入回收站 7 天;YottaCode 不纳入接管);install-self / where 把元阁管理引擎独立安装(默认 ~/.yottaskills/yotta-skills,--dir 指定)并查看运行位置;hub config get/set/clear/rollback 持久化 Hub 位置(config.json;优先级 --hub > YOTTA_SKILLS_HUB > config > 默认;--move 迁移旧 Hub + 重链 + 回收站;lastMigration 记录 + 一键回滚 = 位置回退;仅切换且旧 Hub 非空时记录未迁移供面板一键迁移);hub cleanup-backups 清理历史备份 / 暂存残留(回收站 7 天);hub install / update 把技能装到 ~/.yottaskills/hub 单点真源(清单 27 + 特殊家族 5,特殊家族跟随各自 npm latest;update / hub update 只升不降,--force 才显式降级);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-self 独立安装 / where 位置查看 / install / update / update --check(只读检查)/ update --check --scheduled(后台周检)/ update --auto(家族自动更新)/ hook 适配 / --inventory / --reindex / --dry-run 预览 / --pin(默认)/ --range。触发:需要批量安装或更新元阁全家技能、单点安装并分发到多个智能体、查看本机装了哪些智能体与技能目录、接管非元技能、按场景组合多个元技能、路由或判断该用哪些技能、判断哪些技能值得长期记忆、查看或记录技能使用信号、盘点或查看本机已装技能、重扫技能注册表、给某个智能体或目录一次性铺齐 yotta-* 技能、评估宿主 hook 能力、预览安装清单、锁版本安装、或用户说 元阁/装全家/一次装齐/yotta-skills/install-all/更新全家/检查更新/自动更新/hook 适配/该用哪个技能/路由技能/记忆裁决/技能该不该记住/盘点技能/查看已装技能/技能 Hub/单点安装/链接分发/接管技能/技能枢纽面板/图形化面板/独立安装元阁/查看元阁位置/持久化 Hub 位置/换 Hub 目录/迁移 Hub/迁移回滚/回滚 Hub 位置/清理备份残留/自定义宿主/残留清理/宿主状态 等。边界(Do NOT trigger):只做「组合策划 + 静态路由建议 + M1 记忆裁决只读建议 + 清单 + 下载 + 落位 + 汇总 + 盘点 + re-index + Hub 本机真源与链接分发 + hook 适配」,不含技能本体、不做技能内容开发、不 -g 污染全局、不自动安装缺失技能、不静默写宿主配置或全局记忆、不自动删除技能或记忆;家族安装先自举或调用元信装前门禁,DO NOT INSTALL 阻断,非元阁家族包不自动安装。
3
+ version: 0.30.0
4
+ description: 元阁 -- 元阁全家技能的总编排策划 + 编排路由 + 一键安装器 + 技能盘点 + 本机技能 Hub + 运行时 hook 适配。Hub 层:hub hosts 只读发现本机已装智能体与技能目录(文件系统优先,不读元忆;已核实 / 自动发现 / 桥接分类 + 状态细分:可用 / 残留(实体未确认)/ 未创建 / 仅标记 / 隐藏标记(ignored,只影响显示));hub hosts add/remove/rename/list/mark/exclude/include/set 注册自定义宿主目录、手动标记、不接管开关与目录覆盖(只注册不建目录、移除不删目录;残留清理 hub hosts remove --purge 默认预览、目录入回收站 7 天;YottaCode 不纳入接管);install-self / where 把元阁管理引擎独立安装(默认 ~/.yottaskills/yotta-skills,--dir 指定)并查看运行位置;hub config get/set/clear/rollback 持久化 Hub 位置(config.json;有技能时 clear 默认阻断、--yes 放行;优先级 --hub > YOTTA_SKILLS_HUB > config > 默认;--move 迁移旧 Hub + 重链 + 回收站;lastMigration 记录 + 一键回滚 = 位置回退;仅切换且旧 Hub 非空时记录未迁移供面板一键迁移);hub cleanup-backups 清理历史备份 / 暂存残留(回收站 7 天);hub install / update 把技能装到 ~/.yottaskills/hub 单点真源(清单 27 + 特殊家族 5,特殊家族跟随各自 npm latest;update / hub update 只升不降,--force 才显式降级);hub link --all 用 Windows junction / POSIX symlink 分发到全部已核实宿主(默认范围;--include-discovered 显式纳入自动发现目录;锁 / 数据桥接目录永不链接),元技能在链接时收敛宿主旧副本(移入 ~/.yottaskills/trash 保留 7 天、版本闸门 Hub ≥ 宿主);hub unlink 只删链接、fail-closed(--dir 无 slug 时以文件系统为真源清理该目录全部 Hub 链接,其它位置链接不动);非元技能不参与更新(用户自行处理);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-self 独立安装 / where 位置查看 / install / update / update --check(只读检查)/ update --check --scheduled(后台周检)/ update --auto(家族自动更新)/ hook 适配 / --inventory / --reindex / --dry-run 预览 / --pin(默认)/ --range。触发:需要批量安装或更新元阁全家技能、单点安装并分发到多个智能体、查看本机装了哪些智能体与技能目录、接管非元技能、按场景组合多个元技能、路由或判断该用哪些技能、判断哪些技能值得长期记忆、查看或记录技能使用信号、盘点或查看本机已装技能、重扫技能注册表、给某个智能体或目录一次性铺齐 yotta-* 技能、评估宿主 hook 能力、预览安装清单、锁版本安装、或用户说 元阁/装全家/一次装齐/yotta-skills/install-all/更新全家/检查更新/自动更新/hook 适配/该用哪个技能/路由技能/记忆裁决/技能该不该记住/盘点技能/查看已装技能/技能 Hub/单点安装/链接分发/接管技能/技能枢纽面板/图形化面板/独立安装元阁/查看元阁位置/持久化 Hub 位置/换 Hub 目录/迁移 Hub/迁移回滚/回滚 Hub 位置/清理备份残留/自定义宿主/残留清理/宿主状态 等。边界(Do NOT trigger):只做「组合策划 + 静态路由建议 + M1 记忆裁决只读建议 + 清单 + 下载 + 落位 + 汇总 + 盘点 + re-index + Hub 本机真源与链接分发 + hook 适配」,不含技能本体、不做技能内容开发、不 -g 污染全局、不自动安装缺失技能、不静默写宿主配置或全局记忆、不自动删除技能或记忆;家族安装先自举或调用元信装前门禁,DO NOT INSTALL 阻断,非元阁家族包不自动安装。
5
5
  license: MIT
6
6
  metadata:
7
7
  zh_name: 元阁
@@ -129,7 +129,7 @@ npx -y @yottameta/yotta-skills hub config set --hub "D:\my-hub" --move
129
129
  npx -y @yottameta/yotta-skills hub config get # 查看生效位置与来源
130
130
  npx -y @yottameta/yotta-skills hub config rollback # 回滚预览(位置回退,用当前内容)
131
131
  npx -y @yottameta/yotta-skills hub config rollback --yes # 执行回滚(反向迁移同内核)
132
- npx -y @yottameta/yotta-skills hub config clear # 清除覆盖
132
+ npx -y @yottameta/yotta-skills hub config clear # 清除覆盖(有技能时默认阻断,确认影响后加 --yes)
133
133
  npx -y @yottameta/yotta-skills hub cleanup-backups --yes # 清理历史备份 / 暂存残留(回收站 7 天)
134
134
  ```
135
135
 
@@ -229,13 +229,15 @@ npx -y @yottameta/yotta-skills hub link --all --include-discovered
229
229
  npx -y @yottameta/yotta-skills hub status
230
230
  npx -y @yottameta/yotta-skills hub doctor
231
231
 
232
- # 5b) 自定义宿主注册(只注册,不创建目录、不改宿主配置;移除注册不删目录)
232
+ # 5b) 自定义宿主注册(只注册,不创建目录、不改宿主配置;移除注册不删目录;改名一步到位、只改显示名)
233
233
  npx -y @yottameta/yotta-skills hub hosts add ~/my-agent/skills --label "我的宿主"
234
+ npx -y @yottameta/yotta-skills hub hosts rename ~/my-agent/skills --label "新名字"
234
235
  npx -y @yottameta/yotta-skills hub hosts list
235
236
  npx -y @yottameta/yotta-skills hub hosts remove ~/my-agent/skills
236
237
 
237
238
  # 5c) 手动标记状态 / 清理残留(清理默认预览;只删指向 Hub 的链接,目录入回收站 7 天)
238
239
  npx -y @yottameta/yotta-skills hub hosts mark ~/my-agent/skills --state orphan
240
+ npx -y @yottameta/yotta-skills hub hosts mark ~/my-agent/skills --state ignored # 隐藏标记(只影响显示)
239
241
  npx -y @yottameta/yotta-skills hub hosts remove ~/my-agent/skills --purge
240
242
  npx -y @yottameta/yotta-skills hub hosts remove ~/my-agent/skills --purge --yes
241
243
 
@@ -268,15 +270,17 @@ npx -y @yottameta/yotta-skills hub remove my-skill --dry-run
268
270
 
269
271
  状态细分(0.29.0 起):每条宿主目录带生命周期状态 —— **可用**(实体已确认:应用标记 /
270
272
  用户注册 / 手动标记)、**残留**(实体未确认;可手动确认后清理)、**未创建**、
271
- **仅标记**(已装实体、技能目录未创建);`hub doctor` 的 `host_state` 检查只读提示,
273
+ **仅标记**(已装实体、技能目录未创建)、**隐藏标记**(`ignored`,只影响列表显示、
274
+ 不拦截行为;行为拦截用「不接管」);`hub doctor` 的 `host_state` 检查只读提示,
272
275
  不判 fail。残留清理 = `hub hosts remove <目录> --purge`(默认预览,`--yes` 执行;
273
276
  只删指向 Hub 的链接,非 Hub 链接 / 非技能内容随目录保留在回收站 7 天)。
277
+ 移除注册 / 不接管后若目录仍有 Hub 链接,会提示剩余条数与 `hub unlink --dir` 清理指引。
274
278
 
275
279
  不接管与目录覆盖(0.29.1 起):`hub hosts exclude <agentId|目录>` 把目标从发现、
276
280
  显示、链接三层全部跳过(`--include-discovered` 也不纳入;已建链接不受影响,可
277
281
  `hub unlink` 清理),`hub hosts include` 恢复接管;`hub hosts set <agentId> --dir <目录>`
278
282
  用户级覆盖内置映射(被覆盖旧目录不再进入默认发现 / 链接),`--clear` 恢复默认。
279
- 面板「宿主矩阵」提供等价动作(不接管 / 恢复接管 / 编辑目录 / 标记可用 / 忽略),
283
+ 面板「宿主矩阵」提供等价动作(不接管 / 恢复接管 / 编辑目录 / 标记可用 / 隐藏标记 / 重命名(自定义注册)),
280
284
  写操作均写 Hub 审计。**YottaCode 不纳入接管**(自带三层技能管理;`.yottacode` 与
281
285
  `YOTTACODE_HOME` 在映射 / 扫描 / 注册 / 标记 / 链接全部排除)。未核实映射显示
282
286
  「映射·未核实」,「自动发现」只用于文件系统扫描结果。
@@ -322,7 +326,7 @@ Hub。默认范围为已核实宿主 ∪ 本技能链接台账;`--agent` / `--
322
326
  | `hub adopt --apply [--include <a,b>] [--force] [--in-place]` | 把选中技能收编进 Hub;输出明示范围与目标 Hub;默认复制保真 + 原目录保留;非元阁来源标注「无更新源」并对严格 YAML 风险只读告警;`--skip-scan` 显式提示跳过装前扫描 |
323
327
  | `hub refresh <slug> --from <path>` | 手动同步非元阁技能(重新收编 + 更新哈希 + 台账) |
324
328
  | `hub link --agent <id> \| --dir <dir> \| --all [--include-discovered]` | 把 Hub 技能链接到指定宿主 / 目录 / 全部已核实宿主(默认范围);`--include-discovered` 显式纳入自动发现目录;元技能链接时收敛旧副本(回收站 7 天);`--dry-run` 只预览(含将收敛明细);`--force` 可替换指向 Hub 之外的链接(仅移除链接本身) |
325
- | `hub unlink --agent <id> \| --dir <dir> \| --all [--include-discovered]` | 只删除链接;lstat + readlink 校验目标在 Hub 内,fail-closed;清理范围外链接用 `--dir <目录>` 精确指定 |
329
+ | `hub unlink --agent <id> \| --dir <dir> \| --all [--include-discovered]` | 只删除链接;lstat + readlink 校验目标在当前 Hub 内,fail-closed;`--dir` 不带 slug 时以文件系统为真源清理该目录全部 Hub 链接(台账缺失也能清理),指向其它位置的链接不动并给出错位警告;dry-run / 执行均输出汇总 |
326
330
  | `hub remove <slug> [--dry-run] [--include-discovered]` | 删除 Hub 技能:全宿主清链接(含死链)+ Hub 目录入回收站(7 天)+ 清 Hub / 链接台账 + 写审计;fail-closed(真副本 / 外部链接保留并报告;链接报错即中止不删 Hub);`--agent` / `--dir` 收窄仅用于死链清理 |
327
331
  | `hub status [--json]` | 查看 Hub 技能来源、版本、链接宿主、异常链接与宿主发现摘要 |
328
332
  | `hub doctor [--json]` | 检查断链 / Hub 目标缺失 / slug 不一致 / 单一真源(只读报告多份副本与版本参差)/ 链接范围(`link_scope` 只读报告范围外链接)/ 目录权限;异常时退出码 1 |
@@ -140,6 +140,12 @@
140
140
  .cmd-detail .note{padding:0 0 8px}
141
141
  .cmd-detail .cmd{margin:0 0 8px}
142
142
 
143
+ /* 0.30.0 UX-05:术语速查 */
144
+ .glossary{border:1px solid var(--line);border-radius:8px;background:var(--panel);padding:0 12px}
145
+ .glossary summary{padding:10px 0}
146
+ .glossary-body{display:grid;gap:6px;padding:0 0 12px;font-size:12px;color:var(--muted)}
147
+ .glossary-body b{color:var(--text)}
148
+
143
149
  @media (max-width:980px){.g4{grid-template-columns:repeat(2,minmax(0,1fr))}}
144
150
  @media (max-width:760px){
145
151
  .shell{grid-template-columns:minmax(0,1fr)}
@@ -211,7 +217,7 @@
211
217
  <button class="btn small" data-goto="records">查看记录</button>
212
218
  <button class="btn small" id="copyHubDir">复制路径</button>
213
219
  <button class="btn small" id="hubConfigBtn">设置新位置</button>
214
- <button class="btn small" id="hubConfigClearBtn" hidden>清除覆盖</button>
220
+ <button class="btn small" id="hubConfigClearBtn" hidden title="清除持久 Hub 位置覆盖,回落默认解析;当前 Hub 有技能时会先展示影响面">清除覆盖</button>
215
221
  </div>
216
222
  </div>
217
223
  <div class="section" id="ovSwitchBanner" hidden></div>
@@ -231,9 +237,21 @@
231
237
  <div class="chips" id="hostStateChips"></div>
232
238
  <button class="btn small" id="hostAddBtn">添加自定义目录</button>
233
239
  </div>
240
+ <details class="glossary section">
241
+ <summary>术语速查</summary>
242
+ <div class="glossary-body">
243
+ <div><b>可用</b>:宿主实体确认、技能目录存在(默认分发范围)。</div>
244
+ <div><b>残留</b>:宿主实体未确认(疑似已卸载);可用「清理」把目录移入回收站。</div>
245
+ <div><b>仅标记</b>:检测到宿主已安装,但技能目录尚未创建。</div>
246
+ <div><b>隐藏标记</b>:只影响列表显示,不拦截任何行为(ignored)。</div>
247
+ <div><b>不接管</b>:发现 / 显示 / 链接三层全部跳过;已有链接不动(真正拦截行为的开关)。</div>
248
+ <div><b>清理</b>:解除指向 Hub 的链接 + 目录移入回收站(保留 7 天可恢复)。</div>
249
+ <div><b>桥接</b>:锁 / 数据目录(如 XDG state),永不作为链接目标。</div>
250
+ </div>
251
+ </details>
234
252
  <div class="section panel">
235
253
  <div class="faint" id="hostSummary"></div>
236
- <table><thead><tr><th>宿主</th><th>技能目录</th><th>范围</th><th>目录技能</th><th>指向 Hub</th><th>异常</th><th>状态</th><th></th></tr></thead><tbody id="hostRows"></tbody></table>
254
+ <table><thead><tr><th>宿主</th><th>技能目录</th><th>范围</th><th>目录技能</th><th title="只统计指向当前生效 Hub 的链接(文件系统为真源);指向其它位置的链接单独标注(配置错位预警)">指向当前 Hub</th><th>异常</th><th>状态</th><th></th></tr></thead><tbody id="hostRows"></tbody></table>
237
255
  <div class="empty" id="hostEmpty" hidden>没有匹配的宿主目录。</div>
238
256
  </div>
239
257
  <div class="section panel"><h3>已安装标记(无技能目录的宿主)</h3><div class="list" id="hostMarks"></div></div>
@@ -695,7 +713,20 @@
695
713
 
696
714
  $('#hubConfigBtn').onclick = openHubConfigWizard;
697
715
  $('#hubConfigClearBtn').onclick = function () {
698
- confirmModal('清除 Hub 位置覆盖?', '<p class="label">将删除 config.json 中的 Hub 覆盖,恢复 --hub &gt; 环境变量 &gt; 默认位置的解析。不会移动任何文件。</p>', '清除覆盖', true, async function () {
716
+ var d = state.overview || {};
717
+ var hubInfo = d.hub || {};
718
+ var skills = (d.status && d.status.summary && d.status.summary.skills) || 0;
719
+ var body = '<p class="label">将删除 config.json 中的 Hub 覆盖,恢复 --hub &gt; 环境变量 &gt; 默认位置的解析。不会移动任何文件。</p>' +
720
+ '<div class="result"><span class="badge warn">影响面</span><b>' + esc(skills) + '</b><span>个技能与已建链接仍在 <span class="mono">' + esc(hubInfo.configured || d.hubDir || '') + '</span>;清除后生效位置回落到默认 <span class="mono">' + esc(hubInfo.defaultDir || '默认位置') + '</span>(可能是空目录),面板 / CLI 将按新位置工作。</span></div>' +
721
+ '<p class="faint">替代路径:迁移到默认位置或其它位置,用「设置新位置」向导(等价 CLI hub config set --hub &lt;路径&gt; --move)。</p>';
722
+ if (skills > 0) {
723
+ body += '<div class="section"><label class="label">输入 clear 确认(当前 Hub 有技能):</label><input id="hubClearConfirm" placeholder="clear"></div>';
724
+ }
725
+ confirmModal('清除 Hub 位置覆盖?', body, '清除覆盖', true, async function () {
726
+ if (skills > 0) {
727
+ var typed = ($('#hubClearConfirm') && $('#hubClearConfirm').value || '').trim();
728
+ if (typed !== 'clear') { toast('确认串不匹配(输入 clear)'); return; }
729
+ }
699
730
  var r = await api('/api/hub/config', { body: { clear: true, confirm: 'hub-config' } });
700
731
  if (r.error) { toast(r.error); return; }
701
732
  closeModal();
@@ -756,7 +787,7 @@
756
787
  if (d.error) { toast(d.error); return; }
757
788
  state.hosts = d;
758
789
  var s = d.summary.states || {};
759
- $('#hostSummary').textContent = '可用 ' + (s.available || 0) + ' · 残留 ' + (s.orphan || 0) + ' · 未创建 ' + (s.missing || 0) + ' · 仅标记 ' + (s.markerOnly || 0) + (s.ignored ? ' · 已忽略 ' + s.ignored : '') + (d.summary.excluded ? ' · 不接管 ' + d.summary.excluded : '') + ' · 链接 ' + d.summary.links + '(异常 ' + d.summary.brokenLinks + ')';
790
+ $('#hostSummary').textContent = '可用 ' + (s.available || 0) + ' · 残留 ' + (s.orphan || 0) + ' · 未创建 ' + (s.missing || 0) + ' · 仅标记 ' + (s.markerOnly || 0) + (s.ignored ? ' · 隐藏标记 ' + s.ignored : '') + (d.summary.excluded ? ' · 不接管 ' + d.summary.excluded : '') + ' · 链接 ' + d.summary.links + '(异常 ' + d.summary.brokenLinks + ')' + (d.summary.otherHubLinks ? ' · 指向其它 Hub ' + d.summary.otherHubLinks : '');
760
791
  renderHostStateChips();
761
792
  renderHosts();
762
793
  var marks = d.unpaired || [];
@@ -780,6 +811,7 @@
780
811
  ['orphan', '残留 ' + (s.orphan || 0)],
781
812
  ['missing', '未创建 ' + (s.missing || 0)],
782
813
  ['marker-only', '仅标记 ' + (s.markerOnly || 0)],
814
+ ['ignored', '隐藏标记 ' + (s.ignored || 0)],
783
815
  ];
784
816
  $('#hostStateChips').innerHTML = chips.map(function (c) {
785
817
  return '<button class="chip' + (state.hostStateFilter === c[0] ? ' active' : '') + '" data-host-state="' + esc(c[0]) + '">' + esc(c[1]) + '</button>';
@@ -787,7 +819,7 @@
787
819
  }
788
820
  function hostStateBadge(h) {
789
821
  if (h.bridgeOnly) return '<span class="badge info">桥接</span>';
790
- var map = { available: ['ok', '可用'], orphan: ['warn', '残留'], missing: ['', '未创建'], 'marker-only': ['info', '仅标记'], ignored: ['', '已忽略'] };
822
+ var map = { available: ['ok', '可用'], orphan: ['warn', '残留'], missing: ['', '未创建'], 'marker-only': ['info', '仅标记'], ignored: ['', '隐藏标记'] };
791
823
  var item = map[h.state] || ['', h.state || '未知'];
792
824
  return '<span class="badge ' + item[0] + '">' + esc(item[1]) + (h.stateSource === 'manual' ? '·手动' : '') + '</span>' + (h.brokenLinks ? ' <span class="badge danger">异常</span>' : '');
793
825
  }
@@ -810,19 +842,25 @@
810
842
  : (h.verified ? '<span class="badge ok">已核实</span>'
811
843
  : ((h.detection === 'discovered' || h.detection === 'workspace') ? '<span class="badge warn">自动发现</span>' : '<span class="badge warn">映射·未核实</span>'))));
812
844
  var action = '';
813
- if (h.exists && h.state === 'orphan' && !h.bridgeOnly) action += '<button class="btn small danger" data-host-purge="' + esc(h.dir) + '">清理</button> ';
814
- if (h.state === 'ignored') action += '<button class="btn small" data-host-mark="' + esc(h.dir) + '" data-mark-state="available">标记可用</button> ';
815
- else if (h.exists && !h.bridgeOnly) action += '<button class="btn small" data-host-mark="' + esc(h.dir) + '" data-mark-state="ignored">忽略</button> ';
845
+ if (h.exists && h.state === 'orphan' && !h.bridgeOnly) action += '<button class="btn small danger" data-host-purge="' + esc(h.dir) + '" title="清理残留:解除指向 Hub 的链接 + 目录移入回收站(7 天可恢复)">清理</button> ';
846
+ if (h.state === 'ignored') action += '<button class="btn small" data-host-mark="' + esc(h.dir) + '" data-mark-state="available" title="恢复为可用状态(仅状态标记)">标记可用</button> ';
847
+ else if (h.exists && !h.bridgeOnly) action += '<button class="btn small" data-host-mark="' + esc(h.dir) + '" data-mark-state="ignored" title="只影响列表显示,不拦截行为(拦截用「不接管」)">隐藏标记</button> ';
816
848
  if (h.agentId && !h.bridgeOnly) {
817
- if (overrideIds[String(h.agentId).toLowerCase()]) action += '<button class="btn small" data-host-clear="' + esc(h.agentId) + '">恢复默认</button> ';
818
- else action += '<button class="btn small" data-host-edit="' + esc(h.agentId) + '" data-host-dir="' + esc(h.dir) + '" data-host-label="' + esc(h.label) + '">编辑目录</button> ';
849
+ if (overrideIds[String(h.agentId).toLowerCase()]) action += '<button class="btn small" data-host-clear="' + esc(h.agentId) + '" title="删除目录覆盖,恢复内置映射">恢复默认</button> ';
850
+ else action += '<button class="btn small" data-host-edit="' + esc(h.agentId) + '" data-host-dir="' + esc(h.dir) + '" data-host-label="' + esc(h.label) + '" title="写入目录覆盖(hosts.json),不改宿主配置">编辑目录</button> ';
851
+ }
852
+ if (!h.bridgeOnly) action += '<button class="btn small" data-host-exclude="' + esc(h.agentId || h.dir) + '" data-host-links="' + esc(h.hubLinks || 0) + '" title="加入不接管名单:发现 / 显示 / 链接三层跳过;已有链接不动">不接管</button> ';
853
+ if (h.detection === 'user-registry') action += '<button class="btn small" data-host-rename="' + esc(h.dir) + '" data-host-label="' + esc(h.label) + '" title="只改显示名;目录与手动状态保持">重命名</button> ';
854
+ if (h.detection === 'user-registry') action += '<button class="btn small" data-host-unregister="' + esc(h.dir) + '" data-host-links="' + esc(h.hubLinks || 0) + '" title="只移除注册,不删目录;已有链接不清理(用 hub unlink --dir)">移除注册</button>';
855
+ var linksCell = esc(h.hubLinks || 0);
856
+ if (h.hubLinksOther) {
857
+ var otherTitle = '指向其它位置的链接(配置错位预警)' + ((h.hubLinksOtherRoots || []).length ? ':' + h.hubLinksOtherRoots.join(' / ') : '');
858
+ linksCell += ' <span class="badge warn" title="' + esc(otherTitle) + '">其它 Hub ' + esc(h.hubLinksOther) + '</span>';
819
859
  }
820
- if (!h.bridgeOnly) action += '<button class="btn small" data-host-exclude="' + esc(h.agentId || h.dir) + '">不接管</button> ';
821
- if (h.detection === 'user-registry') action += '<button class="btn small" data-host-unregister="' + esc(h.dir) + '">移除注册</button>';
822
860
  return '<tr><td><div class="cell-main">' + esc(h.label) + '</div><div class="cell-sub">' + esc(h.agentId || (h.known ? '已知宿主' : h.detection || '自动发现')) + '</div></td>' +
823
861
  '<td><div class="mono">' + esc(h.dir) + '</div><button class="btn small" data-copy="' + esc(h.dir) + '">复制</button></td>' +
824
862
  '<td>' + scopeBadge + '</td>' +
825
- '<td>' + esc(h.skillCount || 0) + '</td><td>' + esc(h.hubLinks || 0) + '</td><td>' + esc(h.brokenLinks || 0) + '</td><td>' + hostStateBadge(h) + '</td><td>' + action + '</td></tr>';
863
+ '<td>' + esc(h.skillCount || 0) + '</td><td>' + linksCell + '</td><td>' + esc(h.brokenLinks || 0) + '</td><td>' + hostStateBadge(h) + '</td><td>' + action + '</td></tr>';
826
864
  }).join('');
827
865
  }
828
866
  $('#hostSearch').addEventListener('input', function (e) { state.hostFilter = e.target.value; renderHosts(); });
@@ -906,10 +944,36 @@
906
944
  });
907
945
  return;
908
946
  }
947
+ var renameBtn = e.target.closest('[data-host-rename]');
948
+ if (renameBtn) {
949
+ var renameDir = renameBtn.dataset.hostRename;
950
+ var oldLabel = renameBtn.dataset.hostLabel;
951
+ openModal('重命名宿主 · ' + esc(oldLabel),
952
+ '<p class="label">只改显示名;目录、手动状态与目录覆盖保持不动。</p>' +
953
+ '<div class="section"><label class="label">新名称</label><input id="hostRenameLabel" value="' + esc(oldLabel) + '"></div>', {
954
+ okText: '保存',
955
+ onOk: async function () {
956
+ var value = ($('#hostRenameLabel') && $('#hostRenameLabel').value || '').trim();
957
+ if (!value) { toast('请填写新名称'); return; }
958
+ var r = await api('/api/hosts/rename', { body: { dir: renameDir, label: value } });
959
+ if (r.error) { toast(r.error); return; }
960
+ closeModal();
961
+ toast('已重命名:' + (r.previous ? r.previous + ' → ' : '') + value);
962
+ loadHosts();
963
+ },
964
+ });
965
+ return;
966
+ }
909
967
  var excludeBtn = e.target.closest('[data-host-exclude]');
910
968
  if (excludeBtn) {
911
969
  var excludeTarget = excludeBtn.dataset.hostExclude;
912
- confirmModal('加入「不接管」名单?', '<p class="label">该宿主 / 目录将从发现、显示、链接三层全部跳过;已建立的链接不受影响(可用 CLI hub unlink 清理)。</p><div class="mono">' + esc(excludeTarget) + '</div>', '不接管', false, async function () {
970
+ var excludeLinks = Number(excludeBtn.dataset.hostLinks || 0);
971
+ var excludeBody = '<p class="label">该宿主 / 目录将从发现、显示、链接三层全部跳过(--include-discovered 也不纳入);已有链接不受影响。</p>' +
972
+ (excludeLinks > 0
973
+ ? '<div class="result"><span class="badge warn">影响面</span><b>' + esc(excludeLinks) + '</b><span>条链接仍在该目录(用 hub unlink --dir 或「链接与体检」逐条解除)</span></div>'
974
+ : '<p class="faint">该目录当前没有指向 Hub 的链接。</p>') +
975
+ '<div class="mono">' + esc(excludeTarget) + '</div>';
976
+ confirmModal('加入「不接管」名单?', excludeBody, '不接管', false, async function () {
913
977
  var r = await api('/api/hosts/exclude', { body: { target: excludeTarget } });
914
978
  if (r.error) { toast(r.error); return; }
915
979
  closeModal();
@@ -922,14 +986,21 @@
922
986
  if (markBtn) {
923
987
  var r = await api('/api/hosts/mark', { body: { dir: markBtn.dataset.hostMark, state: markBtn.dataset.markState } });
924
988
  if (r.error) { toast(r.error); return; }
925
- toast('已标记:' + markBtn.dataset.markState);
989
+ var markLabels = { available: '可用', orphan: '残留', ignored: '隐藏标记' };
990
+ toast('已标记:' + (markLabels[markBtn.dataset.markState] || markBtn.dataset.markState));
926
991
  loadHosts();
927
992
  return;
928
993
  }
929
994
  var unregBtn = e.target.closest('[data-host-unregister]');
930
995
  if (unregBtn) {
931
996
  var dir = unregBtn.dataset.hostUnregister;
932
- confirmModal('移除注册?', '<p class="label">只移除自定义宿主注册,不删除目录、不改宿主配置。</p><div class="mono">' + esc(dir) + '</div>', '移除注册', false, async function () {
997
+ var unregLinks = Number(unregBtn.dataset.hostLinks || 0);
998
+ var unregBody = '<p class="label">只移除自定义宿主注册,不删除目录、不改宿主配置。</p>' +
999
+ (unregLinks > 0
1000
+ ? '<div class="result"><span class="badge warn">影响面</span><b>' + esc(unregLinks) + '</b><span>条链接仍在该目录(移除注册不清理链接;用 hub unlink --dir 清理)</span></div>'
1001
+ : '<p class="faint">该目录当前没有指向 Hub 的链接。</p>') +
1002
+ '<div class="mono">' + esc(dir) + '</div>';
1003
+ confirmModal('移除注册?', unregBody, '移除注册', false, async function () {
933
1004
  var r = await api('/api/hosts/remove', { body: { dir: dir } });
934
1005
  if (r.error) { toast(r.error); return; }
935
1006
  closeModal();
@@ -1029,7 +1100,7 @@
1029
1100
  $('#linkTargets').innerHTML = existing.length ? existing.map(function (h) {
1030
1101
  var scopeBadge = h.bridgeOnly ? '<span class="badge info">桥接·不链接</span>' : h.verified ? '<span class="badge ok">已核实</span>' : '<span class="badge warn">自动发现</span>';
1031
1102
  var action = h.bridgeOnly ? '' : '<button class="btn small" data-link-plan="' + esc(h.dir) + '" data-label="' + esc(h.label) + '">预览链接</button>';
1032
- return '<div class="row"><div class="row-main"><div class="row-title">' + esc(h.label) + ' ' + scopeBadge + '</div><div class="row-meta mono">' + esc(h.dir) + '</div><div class="row-meta">目录技能 ' + esc(h.skillCount || 0) + ' · 指向 Hub ' + esc(h.hubLinks || 0) + (h.brokenLinks ? ' · 异常 ' + h.brokenLinks : '') + '</div></div><div class="row-actions">' + action + '</div></div>';
1103
+ return '<div class="row"><div class="row-main"><div class="row-title">' + esc(h.label) + ' ' + scopeBadge + '</div><div class="row-meta mono">' + esc(h.dir) + '</div><div class="row-meta">目录技能 ' + esc(h.skillCount || 0) + ' · 指向当前 Hub ' + esc(h.hubLinks || 0) + (h.hubLinksOther ? ' · <span class="badge warn">其它 Hub ' + esc(h.hubLinksOther) + '</span>' : '') + (h.brokenLinks ? ' · 异常 ' + h.brokenLinks : '') + '</div></div><div class="row-actions">' + action + '</div></div>';
1033
1104
  }).join('') : '<div class="empty">尚未发现可用的宿主技能目录。</div>';
1034
1105
  var rows = links.links || [];
1035
1106
  $('#linkEmpty').hidden = rows.length > 0;
@@ -1217,10 +1288,15 @@
1217
1288
  var sOpts = (s.options || []).length
1218
1289
  ? '<div class="note">选项: ' + s.options.map(function (f) { return '<code>' + esc(f) + '</code>'; }).join(' ') + '</div>'
1219
1290
  : '';
1220
- return '<div class="cmd"><code>' + esc('yotta-skills ' + s.usage) + '</code><span class="why">' + esc(s.what || '') + '</span>' + sOpts + '<button class="btn small" data-copy-cmd="' + esc('yotta-skills ' + s.usage) + '">复制</button></div>';
1291
+ return '<div class="cmd"><code>' + esc('yotta-skills ' + s.usage) + '</code><span class="why">' + esc(s.what || '') + '</span>' + sOpts +
1292
+ (s.impact ? '<div class="note">' + esc(s.impact) + '</div>' : '') +
1293
+ (s.alternative ? '<div class="note">' + esc(s.alternative) + '</div>' : '') +
1294
+ '<button class="btn small" data-copy-cmd="' + esc('yotta-skills ' + s.usage) + '">复制</button></div>';
1221
1295
  }).join('');
1222
1296
  return '<details class="cmd-detail"><summary><code>' + esc('yotta-skills ' + c.usage) + '</code> <span class="why">' + esc(c.what || '') + '</span></summary>' +
1223
- (c.when ? '<div class="note">' + esc(c.when) + '</div>' : '') + opts + subs + '</details>';
1297
+ (c.when ? '<div class="note">' + esc(c.when) + '</div>' : '') +
1298
+ (c.impact ? '<div class="note">' + esc(c.impact) + '</div>' : '') +
1299
+ (c.alternative ? '<div class="note">' + esc(c.alternative) + '</div>' : '') + opts + subs + '</details>';
1224
1300
  }).join('') + '</div>';
1225
1301
  }).join('');
1226
1302
  full += '<div class="cmd-group"><h3>完整选项</h3>' + (d.options || []).map(function (o) {