@yottameta/yotta-skills-plugin 0.25.0 → 0.26.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.
- package/.agents/plugins/marketplace.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/yotta-skills/SKILL.md +8 -4
- package/skills/yotta-skills/assets/view.html +731 -0
- package/skills/yotta-skills/bin/yotta-skills.js +71 -45
- package/skills/yotta-skills/lib/agent-dirs.js +1 -1
- package/skills/yotta-skills/lib/hub-scan.js +97 -0
- package/skills/yotta-skills/lib/hub-view-server.js +668 -0
- package/skills/yotta-skills/lib/hub.js +24 -5
- package/skills/yotta-skills/lib/install-snapshot.js +20 -6
- package/skills/yotta-skills/lib/skills-scan.js +1 -1
- package/skills/yotta-skills/references/hub.md +16 -0
- package/skills/yotta-skills/references/skill-list.md +1 -1
- package/skills/yotta-skills/scripts/yotta-skills-mcp.py +1 -1
- package/skills/yotta-skills/skills.json +1 -1
|
@@ -300,11 +300,30 @@ function classifyTarget(target, hubDir) {
|
|
|
300
300
|
return { kind: 'missing' };
|
|
301
301
|
}
|
|
302
302
|
if (stat.isSymbolicLink()) {
|
|
303
|
-
|
|
303
|
+
let real = null;
|
|
304
|
+
let targetExists = false;
|
|
305
|
+
try {
|
|
306
|
+
real = fs.realpathSync.native ? fs.realpathSync.native(target) : fs.realpathSync(target);
|
|
307
|
+
targetExists = true;
|
|
308
|
+
} catch (_) {
|
|
309
|
+
try {
|
|
310
|
+
real = fs.realpathSync(target);
|
|
311
|
+
targetExists = true;
|
|
312
|
+
} catch (_) { /* broken link: fall back to the recorded link value */ }
|
|
313
|
+
}
|
|
314
|
+
if (!real) {
|
|
315
|
+
// A broken junction / symlink no longer resolves. Read the link value so
|
|
316
|
+
// an unlink can still tell whether it points into the hub (fail-closed
|
|
317
|
+
// cleanup path) without treating it as an arbitrary directory.
|
|
318
|
+
let linkValue = null;
|
|
319
|
+
try { linkValue = fs.readlinkSync(target); } catch (_) { linkValue = null; }
|
|
320
|
+
real = linkValue ? path.resolve(path.dirname(path.resolve(target)), linkValue) : path.resolve(target);
|
|
321
|
+
}
|
|
304
322
|
return {
|
|
305
323
|
kind: 'link',
|
|
306
324
|
target: real,
|
|
307
325
|
inHub: isInside(hubDir, real),
|
|
326
|
+
targetExists,
|
|
308
327
|
};
|
|
309
328
|
}
|
|
310
329
|
if (stat.isDirectory()) return { kind: 'directory' };
|
|
@@ -333,7 +352,7 @@ function linkSkills(options) {
|
|
|
333
352
|
const target = path.join(targetDir, slug);
|
|
334
353
|
const current = classifyTarget(target, hubDir);
|
|
335
354
|
let backup = null;
|
|
336
|
-
if (current.kind === 'link' && current.inHub && samePath(current.target, skill.dir)) {
|
|
355
|
+
if (current.kind === 'link' && current.inHub && current.targetExists !== false && samePath(current.target, skill.dir)) {
|
|
337
356
|
results.push({ slug, status: 'linked', target, note: '已链接到 hub' });
|
|
338
357
|
continue;
|
|
339
358
|
}
|
|
@@ -462,9 +481,9 @@ function linkStatus(hubDir) {
|
|
|
462
481
|
const current = classifyTarget(item.target, hubDir);
|
|
463
482
|
return {
|
|
464
483
|
...item,
|
|
465
|
-
status: current.kind === 'link'
|
|
466
|
-
|
|
467
|
-
|
|
484
|
+
status: current.kind === 'link'
|
|
485
|
+
? (!current.inHub ? 'drift' : (current.targetExists === false ? 'broken' : 'ok'))
|
|
486
|
+
: current.kind === 'missing' ? 'broken' : 'drift',
|
|
468
487
|
actual: current,
|
|
469
488
|
};
|
|
470
489
|
});
|
|
@@ -80,10 +80,10 @@ function readVersion(root) {
|
|
|
80
80
|
function validateSnapshot(snapshot) {
|
|
81
81
|
const root = path.resolve(snapshot || '');
|
|
82
82
|
if (!snapshot || !fs.existsSync(root)) {
|
|
83
|
-
return { ok: false, version: null, digest: null, legacy: false, reason: '快照目录不存在' };
|
|
83
|
+
return { ok: false, version: null, digest: null, legacy: false, reason: '快照目录不存在', source: null, createdAt: null };
|
|
84
84
|
}
|
|
85
85
|
if (!fs.statSync(root).isDirectory()) {
|
|
86
|
-
return { ok: false, version: null, digest: null, legacy: false, reason: '快照不是目录' };
|
|
86
|
+
return { ok: false, version: null, digest: null, legacy: false, reason: '快照不是目录', source: null, createdAt: null };
|
|
87
87
|
}
|
|
88
88
|
const metaFile = root + '.meta.json';
|
|
89
89
|
if (fs.existsSync(metaFile)) {
|
|
@@ -91,13 +91,21 @@ function validateSnapshot(snapshot) {
|
|
|
91
91
|
try {
|
|
92
92
|
metadata = JSON.parse(fs.readFileSync(metaFile, 'utf8'));
|
|
93
93
|
} catch (error) {
|
|
94
|
-
return { ok: false, version: null, digest: null, legacy: false, reason: '快照元数据无法解析: ' + error.message };
|
|
94
|
+
return { ok: false, version: null, digest: null, legacy: false, reason: '快照元数据无法解析: ' + error.message, source: null, createdAt: null };
|
|
95
95
|
}
|
|
96
96
|
let current;
|
|
97
97
|
try {
|
|
98
98
|
current = computeTreeDigest(root);
|
|
99
99
|
} catch (error) {
|
|
100
|
-
return {
|
|
100
|
+
return {
|
|
101
|
+
ok: false,
|
|
102
|
+
version: metadata.version || null,
|
|
103
|
+
digest: metadata.digest || null,
|
|
104
|
+
legacy: false,
|
|
105
|
+
reason: '快照不可读: ' + error.message,
|
|
106
|
+
source: metadata.source || null,
|
|
107
|
+
createdAt: metadata.created_at || null,
|
|
108
|
+
};
|
|
101
109
|
}
|
|
102
110
|
if (!metadata.digest || current.digest !== metadata.digest) {
|
|
103
111
|
return {
|
|
@@ -106,6 +114,8 @@ function validateSnapshot(snapshot) {
|
|
|
106
114
|
digest: metadata.digest || null,
|
|
107
115
|
legacy: false,
|
|
108
116
|
reason: '快照摘要不一致,可能已被修改',
|
|
117
|
+
source: metadata.source || null,
|
|
118
|
+
createdAt: metadata.created_at || null,
|
|
109
119
|
};
|
|
110
120
|
}
|
|
111
121
|
return {
|
|
@@ -114,14 +124,16 @@ function validateSnapshot(snapshot) {
|
|
|
114
124
|
digest: metadata.digest,
|
|
115
125
|
legacy: false,
|
|
116
126
|
reason: null,
|
|
127
|
+
source: metadata.source || null,
|
|
128
|
+
createdAt: metadata.created_at || null,
|
|
117
129
|
};
|
|
118
130
|
}
|
|
119
131
|
|
|
120
132
|
const version = readVersion(root);
|
|
121
133
|
if (!fs.existsSync(path.join(root, 'SKILL.md'))) {
|
|
122
|
-
return { ok: false, version, digest: null, legacy: true, reason: '旧快照缺少 SKILL.md' };
|
|
134
|
+
return { ok: false, version, digest: null, legacy: true, reason: '旧快照缺少 SKILL.md', source: null, createdAt: null };
|
|
123
135
|
}
|
|
124
|
-
return { ok: true, version, digest: null, legacy: true, reason: null };
|
|
136
|
+
return { ok: true, version, digest: null, legacy: true, reason: null, source: null, createdAt: null };
|
|
125
137
|
}
|
|
126
138
|
|
|
127
139
|
function createSnapshot(target, options) {
|
|
@@ -191,6 +203,8 @@ function listSnapshots(homeDir, slug) {
|
|
|
191
203
|
version: validation.version,
|
|
192
204
|
legacy: validation.legacy,
|
|
193
205
|
reason: validation.reason,
|
|
206
|
+
source: validation.source || null,
|
|
207
|
+
createdAt: validation.createdAt || null,
|
|
194
208
|
mtime_ms: mtimeMs,
|
|
195
209
|
});
|
|
196
210
|
}
|
|
@@ -170,7 +170,7 @@ function opencodeUserDir() {
|
|
|
170
170
|
return agentDirs.resolveUserDir('.config/opencode/skills', { homeDir: os.homedir(), env: process.env });
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
/** OpenClaw
|
|
173
|
+
/** OpenClaw 用户级技能目录:$OPENCLAW_STATE_DIR/skills(默认 ~/.openclaw/skills)。 */
|
|
174
174
|
function openclawUserDir() {
|
|
175
175
|
return agentDirs.resolveUserDir('.openclaw/skills', { homeDir: os.homedir(), env: process.env });
|
|
176
176
|
}
|
|
@@ -59,6 +59,22 @@ npx -y @yottameta/yotta-skills hub doctor
|
|
|
59
59
|
- 官方锁文件:只读兼容 Vercel Labs `skills` CLI 的 `.skill-lock.json` v3
|
|
60
60
|
(`$XDG_STATE_HOME/skills/` 或 `~/.agents/`)。
|
|
61
61
|
|
|
62
|
+
## 本地面板(yotta-skills view)
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx -y @yottameta/yotta-skills view
|
|
66
|
+
npx -y @yottameta/yotta-skills view --port 8790
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
面板只在 `127.0.0.1`(默认 8789)监听,零远程资源、零遥测,只读写 Hub
|
|
70
|
+
目录与台账 / 证据文件,不读元忆、不改宿主全局配置。六个视图:概览、宿主矩阵、
|
|
71
|
+
收编向导、链接与体检、记录与回滚、路由与编排,另附高级 CLI 页。
|
|
72
|
+
|
|
73
|
+
动作边界:收编 / 链接 / 解除 / 回滚可执行(预览 → 确认 → 执行 → 证据);
|
|
74
|
+
install / update / refresh 只在高级 CLI 页给出可复制命令,不在网页执行。
|
|
75
|
+
写操作需要页面会话令牌,解除与回滚需要破坏性确认串;收编始终逐项运行元信
|
|
76
|
+
扫描,high / critical 阻断。
|
|
77
|
+
|
|
62
78
|
## 安全边界
|
|
63
79
|
|
|
64
80
|
- `unlink` 只删除 lstat 确认为链接、且 readlink 目标位于 Hub 内的路径。
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
| slug | 中文名 | npm 包 | 清单版本 | 家族 | 说明 |
|
|
7
7
|
|---|---|---|---|---|
|
|
8
8
|
| `yotta-anti-shallow` | 元谨 | `@yottameta/yotta-anti-shallow` | 1.4.2 | quality | 防 AI 敷衍规则引擎(深入分析/根因追溯时激活) |
|
|
9
|
-
| `yotta-code-quality` | 元质 | `@yottameta/yotta-code-quality` | 0.4.
|
|
9
|
+
| `yotta-code-quality` | 元质 | `@yottameta/yotta-code-quality` | 0.4.3 | quality | 结对式代码质量评审(十二类腐化风险 + 0-100 健康分) |
|
|
10
10
|
| `yotta-workflow` | 元序 | `@yottameta/yotta-workflow` | 0.4.3 | workflow | 跨会话/跨项目通用工作流协议(状态就近存) |
|
|
11
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/ 条目 |
|