@wipcomputer/wip-ldm-os 0.4.80 → 0.4.81
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/SKILL.md +1 -1
- package/lib/deploy.mjs +57 -0
- package/package.json +2 -1
- package/scripts/test-skill-frontmatter.mjs +44 -0
package/SKILL.md
CHANGED
|
@@ -9,7 +9,7 @@ license: MIT
|
|
|
9
9
|
compatibility: Requires git, npm, node. Node.js 18+.
|
|
10
10
|
metadata:
|
|
11
11
|
display-name: "LDM OS"
|
|
12
|
-
version: "0.4.
|
|
12
|
+
version: "0.4.81"
|
|
13
13
|
homepage: "https://github.com/wipcomputer/wip-ldm-os"
|
|
14
14
|
author: "Parker Todd Brooks"
|
|
15
15
|
category: infrastructure
|
package/lib/deploy.mjs
CHANGED
|
@@ -59,6 +59,57 @@ function writeJSON(path, data) {
|
|
|
59
59
|
writeFileSync(path, JSON.stringify(data, null, 2) + '\n');
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
export function validateSkillFrontmatter(path) {
|
|
63
|
+
let content;
|
|
64
|
+
try {
|
|
65
|
+
content = readFileSync(path, 'utf8');
|
|
66
|
+
} catch (e) {
|
|
67
|
+
return { ok: false, line: 1, message: `cannot read SKILL.md: ${e.message}` };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const lines = content.split(/\r?\n/);
|
|
71
|
+
if (lines[0] !== '---') {
|
|
72
|
+
return { ok: false, line: 1, message: 'SKILL.md must start with YAML frontmatter' };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let end = -1;
|
|
76
|
+
for (let i = 1; i < lines.length; i++) {
|
|
77
|
+
if (lines[i] === '---') {
|
|
78
|
+
end = i;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (end === -1) {
|
|
83
|
+
return { ok: false, line: 1, message: 'SKILL.md frontmatter is missing a closing --- marker' };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
for (let i = 1; i < end; i++) {
|
|
87
|
+
const line = lines[i];
|
|
88
|
+
const trimmed = line.trim();
|
|
89
|
+
if (!trimmed || trimmed.startsWith('#') || /^\s/.test(line)) continue;
|
|
90
|
+
|
|
91
|
+
const match = /^([A-Za-z0-9_-]+):(?:\s*(.*))?$/.exec(line);
|
|
92
|
+
if (!match) {
|
|
93
|
+
return { ok: false, line: i + 1, message: 'frontmatter line is not a simple key/value mapping' };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const value = match[2] || '';
|
|
97
|
+
const valueTrimmed = value.trimStart();
|
|
98
|
+
const first = valueTrimmed[0] || '';
|
|
99
|
+
const isQuoted = first === '"' || first === "'";
|
|
100
|
+
const isStructured = first === '[' || first === '{' || first === '|' || first === '>';
|
|
101
|
+
if (valueTrimmed.includes(': ') && !isQuoted && !isStructured) {
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
line: i + 1,
|
|
105
|
+
message: 'value contains an unquoted colon; quote the scalar value',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return { ok: true };
|
|
111
|
+
}
|
|
112
|
+
|
|
62
113
|
function ensureBinExecutable(binNames) {
|
|
63
114
|
try {
|
|
64
115
|
const npmPrefix = execSync('npm config get prefix', { encoding: 'utf8' }).trim();
|
|
@@ -1195,6 +1246,12 @@ function installSkill(repoPath, toolName) {
|
|
|
1195
1246
|
if (!existsSync(skillSrc) && existsSync(permanentSkill)) skillSrc = permanentSkill;
|
|
1196
1247
|
if (!existsSync(skillSrc)) return false;
|
|
1197
1248
|
|
|
1249
|
+
const frontmatter = validateSkillFrontmatter(skillSrc);
|
|
1250
|
+
if (!frontmatter.ok) {
|
|
1251
|
+
fail(`Skill: invalid SKILL.md frontmatter at ${skillSrc}:${frontmatter.line}: ${frontmatter.message}`);
|
|
1252
|
+
return false;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1198
1255
|
// Find references/ source: repo path first, then permanent copy
|
|
1199
1256
|
let refsSrc = join(repoPath, 'references');
|
|
1200
1257
|
const permanentRefs = join(LDM_EXTENSIONS, toolName, 'references');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wipcomputer/wip-ldm-os",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.81",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "LDM OS: identity, memory, and sovereignty infrastructure for AI agents",
|
|
6
6
|
"engines": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"build:bridge": "cd src/bridge && npm install && npx tsup core.ts mcp-server.ts cli.ts openclaw.ts --format esm --dts --clean --outDir ../../dist/bridge",
|
|
20
20
|
"build": "npm run build:bridge",
|
|
21
21
|
"prepublishOnly": "npm run build:bridge",
|
|
22
|
+
"test:skill-frontmatter": "node scripts/test-skill-frontmatter.mjs",
|
|
22
23
|
"fmt": "npx prettier --write 'src/**/*.{ts,mjs}' 'lib/**/*.mjs' 'bin/**/*.js'",
|
|
23
24
|
"fmt:check": "npx prettier --check 'src/**/*.{ts,mjs}' 'lib/**/*.mjs' 'bin/**/*.js'"
|
|
24
25
|
},
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdtempSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { validateSkillFrontmatter } from '../lib/deploy.mjs';
|
|
6
|
+
|
|
7
|
+
const dir = mkdtempSync(join(tmpdir(), 'ldm-skill-frontmatter-'));
|
|
8
|
+
const bad = join(dir, 'bad-SKILL.md');
|
|
9
|
+
const good = join(dir, 'good-SKILL.md');
|
|
10
|
+
|
|
11
|
+
writeFileSync(bad, [
|
|
12
|
+
'---',
|
|
13
|
+
'name: bad',
|
|
14
|
+
'description: Read when: guard blocks a tool call',
|
|
15
|
+
'---',
|
|
16
|
+
'',
|
|
17
|
+
'# Bad',
|
|
18
|
+
'',
|
|
19
|
+
].join('\n'));
|
|
20
|
+
|
|
21
|
+
writeFileSync(good, [
|
|
22
|
+
'---',
|
|
23
|
+
'name: good',
|
|
24
|
+
'description: "Read when: guard blocks a tool call"',
|
|
25
|
+
'---',
|
|
26
|
+
'',
|
|
27
|
+
'# Good',
|
|
28
|
+
'',
|
|
29
|
+
].join('\n'));
|
|
30
|
+
|
|
31
|
+
const badResult = validateSkillFrontmatter(bad);
|
|
32
|
+
if (badResult.ok) {
|
|
33
|
+
throw new Error('expected unquoted colon frontmatter to be rejected');
|
|
34
|
+
}
|
|
35
|
+
if (badResult.line !== 3) {
|
|
36
|
+
throw new Error(`expected failure on line 3, got line ${badResult.line}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const goodResult = validateSkillFrontmatter(good);
|
|
40
|
+
if (!goodResult.ok) {
|
|
41
|
+
throw new Error(`expected quoted frontmatter to pass: ${goodResult.message}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log('skill frontmatter regression passed');
|