@yottameta/yotta-skills-plugin 0.24.1 → 0.25.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.
@@ -17,33 +17,15 @@
17
17
  const fs = require('fs');
18
18
  const os = require('os');
19
19
  const path = require('path');
20
+ const agentDirs = require('./agent-dirs');
20
21
 
21
22
  const REGISTRY_DIR_NAME = '.yottaskills';
22
23
  const REGISTRY_FILE_NAME = 'registry.json';
23
24
  const SKILL_FILE = 'SKILL.md';
24
25
 
25
- // 智能体 -> 用户级默认技能目录(与 bin/yotta-skills.js AGENT_DIRS 同源;
26
- // 依据 agent-skill-dir-map 权威映射,2026-08-23 核实)
27
- const AGENT_DIRS = {
28
- claude: { label: 'Claude Code', dirs: ['.claude/skills'] },
29
- cursor: { label: 'Cursor', dirs: ['.cursor/skills', '.agents/skills'] },
30
- codex: { label: 'Codex', dirs: [] }, // 特判:$CODEX_HOME/skills
31
- gemini: { label: 'Gemini CLI', dirs: ['.gemini/skills', '.agents/skills'] },
32
- goose: { label: 'Goose', dirs: ['.config/goose/skills', '.agents/skills'] },
33
- amp: { label: 'Amp', dirs: ['.config/agents/skills', '.agents/skills'] },
34
- opencode: { label: 'OpenCode', dirs: [] }, // 特判:$XDG_CONFIG_HOME
35
- windsurf: { label: 'Windsurf', dirs: ['.codeium/windsurf/skills'] },
36
- workbuddy: { label: 'WorkBuddy', dirs: ['.workbuddy/skills'] },
37
- kiro: { label: 'Kiro', dirs: ['.kiro/skills'] },
38
- trae: { label: 'Trae Code CLI', dirs: ['.traecli/skills'] },
39
- 'trae-cn': { label: 'Trae IDE(国内)', dirs: ['.trae-cn/skills'] },
40
- qwen: { label: 'Qwen Code', dirs: ['.qwen/skills'] },
41
- comate: { label: 'Comate 文心快码', dirs: ['.comate/skills'] },
42
- codebuddy: { label: 'CodeBuddy Code', dirs: ['.codebuddy/skills'] },
43
- kimi: { label: 'Kimi Code CLI', dirs: ['.kimi/skills'] },
44
- openclaw: { label: 'OpenClaw / QClaw', dirs: ['.openclaw/skills'] }, // 特判:$OPENCLAW_STATE_DIR
45
- agents: { label: '通用 AGENTS.md', dirs: ['.agents/skills'] },
46
- };
26
+ // 智能体 -> 用户级默认技能目录。单一真源在 lib/agent-dirs.js
27
+ // (兼容 Vercel Labs `skills` CLI / agentskills.io 的宿主表,MIT)。
28
+ const AGENT_DIRS = agentDirs.AGENT_DIRS;
47
29
 
48
30
  /** 解析 SKILL.md 前部 YAML frontmatter(name/version/description 单行值)。 */
49
31
  function parseFrontmatter(text) {
@@ -153,8 +135,13 @@ function scanSkillDir(dir) {
153
135
  return results;
154
136
  }
155
137
  for (const e of entries) {
156
- if (!e.isDirectory()) continue;
138
+ if (!e.isDirectory() && !e.isSymbolicLink()) continue;
157
139
  const skillDir = path.join(dir, e.name);
140
+ try {
141
+ if (!fs.statSync(skillDir).isDirectory()) continue;
142
+ } catch (_) {
143
+ continue;
144
+ }
158
145
  let text;
159
146
  try {
160
147
  text = fs.readFileSync(path.join(skillDir, SKILL_FILE), 'utf8');
@@ -175,27 +162,21 @@ function scanSkillDir(dir) {
175
162
 
176
163
  /** Codex 用户级技能目录:$CODEX_HOME/skills(默认 ~/.codex/skills)。 */
177
164
  function codexUserDir() {
178
- const base = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
179
- return path.join(base, 'skills');
165
+ return agentDirs.resolveUserDir('.codex/skills', { homeDir: os.homedir(), env: process.env });
180
166
  }
181
167
 
182
168
  /** OpenCode 用户级技能目录:$XDG_CONFIG_HOME/opencode/skills。 */
183
169
  function opencodeUserDir() {
184
- const base = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
185
- return path.join(base, 'opencode', 'skills');
170
+ return agentDirs.resolveUserDir('.config/opencode/skills', { homeDir: os.homedir(), env: process.env });
186
171
  }
187
172
 
188
173
  /** OpenClaw / QClaw 用户级技能目录:$OPENCLAW_STATE_DIR/skills(默认 ~/.openclaw/skills)。 */
189
174
  function openclawUserDir() {
190
- const base = process.env.OPENCLAW_STATE_DIR || path.join(os.homedir(), '.openclaw');
191
- return path.join(base, 'skills');
175
+ return agentDirs.resolveUserDir('.openclaw/skills', { homeDir: os.homedir(), env: process.env });
192
176
  }
193
177
 
194
178
  function resolveUserDir(rel) {
195
- if (rel === '.codex/skills') return codexUserDir();
196
- if (rel === '.config/opencode/skills') return opencodeUserDir();
197
- if (rel === '.openclaw/skills') return openclawUserDir();
198
- return path.join(os.homedir(), rel);
179
+ return agentDirs.resolveUserDir(rel, { homeDir: os.homedir(), env: process.env });
199
180
  }
200
181
 
201
182
  /**
@@ -216,12 +197,9 @@ function defaultRoots(opts) {
216
197
  roots.push({ dir: norm, label });
217
198
  };
218
199
  const home = os.homedir();
219
- for (const key of Object.keys(AGENT_DIRS)) {
220
- const info = AGENT_DIRS[key];
221
- for (const rel of info.dirs) add(resolveUserDir(rel), info.label);
200
+ for (const root of agentDirs.knownRoots({ homeDir: home, env: process.env })) {
201
+ add(root.dir, root.label);
222
202
  }
223
- add(codexUserDir(), 'Codex');
224
- add(opencodeUserDir(), 'OpenCode');
225
203
  if (Array.isArray(opts.extraDirs)) {
226
204
  for (const d of opts.extraDirs) add(d, '指定目录');
227
205
  }
@@ -268,6 +246,11 @@ function scanRoots(roots) {
268
246
  }
269
247
  }
270
248
  for (const item of map.values()) {
249
+ item.variants.sort((a, b) => {
250
+ const byVersion = compareSemver(a.version, b.version);
251
+ if (byVersion) return byVersion;
252
+ return String(a.source || '').localeCompare(String(b.source || ''));
253
+ });
271
254
  const representative = selectRepresentativeVariant(item.variants);
272
255
  if (!representative) continue;
273
256
  item.version = representative.version || '';
@@ -282,6 +265,13 @@ function scanRoots(roots) {
282
265
  }));
283
266
  if (conflicts.length) item.conflicts = conflicts;
284
267
  else delete item.conflicts;
268
+ if (item.conflicts) {
269
+ item.conflicts.sort((a, b) => {
270
+ const byVersion = compareSemver(a.version, b.version);
271
+ if (byVersion) return byVersion;
272
+ return String(a.source || '').localeCompare(String(b.source || ''));
273
+ });
274
+ }
285
275
  }
286
276
  return {
287
277
  generated_at: new Date().toISOString(),
@@ -0,0 +1,66 @@
1
+ # Hub(本机单点安装 + 链接分发)
2
+
3
+ ## 它解决什么
4
+
5
+ 多智能体各自维护技能目录时,同一技能会被复制多份,升级后容易版本漂移。Hub 把
6
+ 技能集中到本机一份真源,再按宿主建立链接(Windows junction / macOS·Linux
7
+ symlink),升级只改真源,已链接宿主即时生效。
8
+
9
+ ## 默认位置
10
+
11
+ - Hub 真源:`~/.yottaskills/hub`
12
+ - 技能台账:`<hub>/.yotta-hub.json`
13
+ - 链接台账:`<hub>/.yotta-links.json`
14
+ - 覆盖方式:`--hub <dir>` 或 `YOTTA_SKILLS_HUB`
15
+
16
+ ## 命令
17
+
18
+ ```bash
19
+ # 发现本机已装智能体与技能目录(文件系统优先;不读元忆 / 注册表)
20
+ npx -y @yottameta/yotta-skills hub hosts
21
+
22
+ # 安装 / 更新 Hub 真源
23
+ npx -y @yottameta/yotta-skills hub install
24
+ npx -y @yottameta/yotta-skills hub update
25
+
26
+ # 接管各宿主现有技能:先只读预演,再复制收编(原目录保留)
27
+ npx -y @yottameta/yotta-skills hub adopt --scan
28
+ npx -y @yottameta/yotta-skills hub adopt --apply
29
+
30
+ # 非元阁来源技能没有统一更新源:手动从指定目录刷新
31
+ npx -y @yottameta/yotta-skills hub refresh my-skill --from <技能目录>
32
+
33
+ # 分发到全部已发现宿主(默认不覆盖真目录;--force 才备份并替换)
34
+ npx -y @yottameta/yotta-skills hub link --all
35
+
36
+ # 只删除链接,不动 Hub 真源
37
+ npx -y @yottameta/yotta-skills hub unlink --all
38
+
39
+ # 查看来源、版本、链接宿主与异常
40
+ npx -y @yottameta/yotta-skills hub status
41
+ npx -y @yottameta/yotta-skills hub doctor
42
+ ```
43
+
44
+ ## 宿主发现口径
45
+
46
+ 1. 已收录宿主映射(兼容 Vercel Labs `skills` CLI / SkillCat 的公开宿主表);
47
+ 2. 环境变量覆盖(`CODEX_HOME`、`XDG_CONFIG_HOME`、`DSH_HOME`、
48
+ `OPENCLAW_STATE_DIR`、`CLAUDE_CONFIG_DIR` 等);
49
+ 3. 常见配置根启发式扫描;
50
+ 4. `YOTTA_SKILLS_DISCOVERY_ROOTS` 显式补充;
51
+ 5. `--dir <path>` 精确指定非标准宿主。
52
+
53
+ 自动发现默认排除临时目录、`.bak`、candidate / staging、插件构建目录等噪声。
54
+
55
+ ## 兼容标准
56
+
57
+ - 技能格式:`agentskills.io` Agent Skills(`SKILL.md` + `name` / `description`)。
58
+ - 通用投影:`~/.agents/skills`、`$XDG_CONFIG_HOME/agents/skills`。
59
+ - 官方锁文件:只读兼容 Vercel Labs `skills` CLI 的 `.skill-lock.json` v3
60
+ (`$XDG_STATE_HOME/skills/` 或 `~/.agents/`)。
61
+
62
+ ## 安全边界
63
+
64
+ - `unlink` 只删除 lstat 确认为链接、且 readlink 目标位于 Hub 内的路径。
65
+ - 真目录 / 非 Hub 链接一律拒绝删除;`--force` 对真目录先备份再替换。
66
+ - Hub 仅本机使用,不对外分发技能内容。
@@ -8,7 +8,7 @@
8
8
  | `yotta-anti-shallow` | 元谨 | `@yottameta/yotta-anti-shallow` | 1.4.2 | quality | 防 AI 敷衍规则引擎(深入分析/根因追溯时激活) |
9
9
  | `yotta-code-quality` | 元质 | `@yottameta/yotta-code-quality` | 0.4.2 | quality | 结对式代码质量评审(十二类腐化风险 + 0-100 健康分) |
10
10
  | `yotta-workflow` | 元序 | `@yottameta/yotta-workflow` | 0.4.3 | workflow | 跨会话/跨项目通用工作流协议(状态就近存) |
11
- | `yotta-memory` | 元忆 | `@yottameta/yotta-memory` | 0.21.0 | memory | 有权限边界的文件式智能体记忆(FACT/PREF/BOUND/COMMIT) |
11
+ | `yotta-memory` | 元忆 | `@yottameta/yotta-memory` | 0.21.1 | memory | 有权限边界的文件式智能体记忆(FACT/PREF/BOUND/COMMIT) |
12
12
  | `yotta-learn` | 元习 | `@yottameta/yotta-learn` | 0.2.2 | memory | 学习沉淀 CLI:错误/纠正/洞见沉淀为 .learnings/ 条目 |
13
13
  | `yotta-security-audit` | 元安 | `@yottameta/yotta-security-audit` | 0.3.1 | security | 安全扫描引擎:13 类检测器 + 系统安全基线 + 学生数据隐私(教育版) |
14
14
  | `yotta-vetter` | 元审 | `@yottameta/yotta-vetter` | 0.2.7 | security | 安全审查协议:四阶段 review + SAFE TO INSTALL 判定 |
@@ -197,10 +197,10 @@
197
197
  ]
198
198
  },
199
199
  "yotta-memory": {
200
- "version": "0.21.0",
201
- "treeHash": "sha256:e4f0fc7f52a7516f1d72cace3afe67730cdf5be45cf892f3814963be44cd7573",
200
+ "version": "0.21.1",
201
+ "treeHash": "sha256:452c1674084e916b3f6e0831f3662eaf2852af0e9a4fea3b0ff6a42c6bec9e71",
202
202
  "files": 18,
203
- "bytes": 1264480,
203
+ "bytes": 1270192,
204
204
  "mode": "strict",
205
205
  "exceptions": [
206
206
  {
@@ -27,7 +27,7 @@ import subprocess
27
27
  import sys
28
28
  from pathlib import Path
29
29
 
30
- VERSION = "0.24.1"
30
+ VERSION = "0.25.0"
31
31
  TOOL_NAME = "yotta-skills"
32
32
  CN_NAME = "元阁"
33
33
  MCP_PROTOCOL_MODERN = "2026-07-28"
@@ -31,7 +31,7 @@
31
31
  "slug": "yotta-memory",
32
32
  "name": "元忆",
33
33
  "pkg": "@yottameta/yotta-memory",
34
- "version": "0.21.0",
34
+ "version": "0.21.1",
35
35
  "domain": "memory",
36
36
  "desc": "有权限边界的文件式智能体记忆(FACT/PREF/BOUND/COMMIT)"
37
37
  },