@wipcomputer/wip-ldm-os 0.4.80 → 0.4.82-alpha.1
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/bin/ldm.js +12 -14
- package/lib/deploy.mjs +57 -0
- package/package.json +3 -1
- package/scripts/test-installer-update-tracks.mjs +39 -0
- package/scripts/test-skill-frontmatter.mjs +44 -0
- package/shared/docs/dev-guide-wipcomputerinc.md.tmpl +3 -1
- package/shared/docs/how-install-works.md.tmpl +2 -0
- package/shared/docs/how-releases-work.md.tmpl +9 -3
- package/shared/rules/release-pipeline.md +4 -2
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/bin/ldm.js
CHANGED
|
@@ -2334,7 +2334,11 @@ async function cmdInstallCatalog() {
|
|
|
2334
2334
|
const currentVersion = matchEntry?.installed?.version || matchEntry?.version || '?';
|
|
2335
2335
|
|
|
2336
2336
|
try {
|
|
2337
|
-
const
|
|
2337
|
+
const npmTag = ALPHA_FLAG ? 'alpha' : BETA_FLAG ? 'beta' : 'latest';
|
|
2338
|
+
const npmViewCmd = npmTag === 'latest'
|
|
2339
|
+
? `npm view ${comp.npm} version 2>/dev/null`
|
|
2340
|
+
: `npm view ${comp.npm} dist-tags.${npmTag} 2>/dev/null`;
|
|
2341
|
+
const latest = execSync(npmViewCmd, {
|
|
2338
2342
|
encoding: 'utf8', timeout: 10000,
|
|
2339
2343
|
}).trim();
|
|
2340
2344
|
if (latest && semverNewer(latest, currentVersion)) {
|
|
@@ -2608,24 +2612,18 @@ async function cmdInstallCatalog() {
|
|
|
2608
2612
|
execSync(`ldm install ${installSource}`, { stdio: 'inherit' });
|
|
2609
2613
|
updated++;
|
|
2610
2614
|
|
|
2611
|
-
// For parent packages,
|
|
2615
|
+
// For parent packages, installFromPath already refreshes each sub-tool
|
|
2616
|
+
// registry entry with that sub-tool's own package version. Do not stamp
|
|
2617
|
+
// the parent package version onto sub-tools; their versions intentionally
|
|
2618
|
+
// differ from the toolbox aggregate version.
|
|
2612
2619
|
if (entry.isParent && entry.registryMatches) {
|
|
2613
2620
|
const registry = readJSON(REGISTRY_PATH);
|
|
2614
2621
|
if (registry?.extensions) {
|
|
2615
|
-
|
|
2622
|
+
let refreshed = 0;
|
|
2616
2623
|
for (const subTool of entry.registryMatches) {
|
|
2617
|
-
if (registry.extensions[subTool])
|
|
2618
|
-
registry.extensions[subTool].version = entry.latestVersion;
|
|
2619
|
-
registry.extensions[subTool].updatedAt = now;
|
|
2620
|
-
// Also update v2 installed block
|
|
2621
|
-
if (registry.extensions[subTool].installed) {
|
|
2622
|
-
registry.extensions[subTool].installed.version = entry.latestVersion;
|
|
2623
|
-
registry.extensions[subTool].installed.updatedAt = now;
|
|
2624
|
-
}
|
|
2625
|
-
}
|
|
2624
|
+
if (registry.extensions[subTool]) refreshed++;
|
|
2626
2625
|
}
|
|
2627
|
-
|
|
2628
|
-
console.log(` + Updated registry for ${entry.registryMatches.length} sub-tools`);
|
|
2626
|
+
console.log(` + Refreshed registry for ${refreshed}/${entry.registryMatches.length} sub-tools`);
|
|
2629
2627
|
}
|
|
2630
2628
|
}
|
|
2631
2629
|
} catch (e) {
|
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.82-alpha.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "LDM OS: identity, memory, and sovereignty infrastructure for AI agents",
|
|
6
6
|
"engines": {
|
|
@@ -19,6 +19,8 @@
|
|
|
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",
|
|
23
|
+
"test:installer-update-tracks": "node scripts/test-installer-update-tracks.mjs",
|
|
22
24
|
"fmt": "npx prettier --write 'src/**/*.{ts,mjs}' 'lib/**/*.mjs' 'bin/**/*.js'",
|
|
23
25
|
"fmt:check": "npx prettier --check 'src/**/*.{ts,mjs}' 'lib/**/*.mjs' 'bin/**/*.js'"
|
|
24
26
|
},
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const root = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
7
|
+
const cli = readFileSync(join(root, 'bin', 'ldm.js'), 'utf8');
|
|
8
|
+
|
|
9
|
+
const marker = '// Check parent packages for toolbox-style repos (#132)';
|
|
10
|
+
const idx = cli.indexOf(marker);
|
|
11
|
+
if (idx === -1) {
|
|
12
|
+
throw new Error('Could not find toolbox parent update block');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const parentBlock = cli.slice(idx, cli.indexOf('const totalUpdates = npmUpdates.length;', idx));
|
|
16
|
+
if (!parentBlock.includes("const npmTag = ALPHA_FLAG ? 'alpha' : BETA_FLAG ? 'beta' : 'latest';")) {
|
|
17
|
+
throw new Error('Toolbox parent update block does not select the requested release track');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!parentBlock.includes('dist-tags.${npmTag}')) {
|
|
21
|
+
throw new Error('Toolbox parent update block does not query alpha/beta dist-tags');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (/const latest = execSync\(`npm view \$\{comp\.npm\} version 2>\/dev\/null`/.test(parentBlock)) {
|
|
25
|
+
throw new Error('Toolbox parent update block still hardcodes the stable npm version query');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const installMarker = '// For parent packages, installFromPath already refreshes each sub-tool';
|
|
29
|
+
const installIdx = cli.indexOf(installMarker);
|
|
30
|
+
if (installIdx === -1) {
|
|
31
|
+
throw new Error('Could not find parent registry refresh block');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const installBlock = cli.slice(installIdx, cli.indexOf('} catch (e) {', installIdx));
|
|
35
|
+
if (installBlock.includes('= entry.latestVersion')) {
|
|
36
|
+
throw new Error('Parent update block must not stamp parent versions onto sub-tool registry entries');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log('installer update-track regression checks passed');
|
|
@@ -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');
|
|
@@ -28,7 +28,9 @@ These are three distinct actions. Never combine them. Never skip the dogfooding
|
|
|
28
28
|
| **Deploy** | Ship to public | `wip-release` (version bump, npm publish, GitHub release) + `deploy-public.sh` (sync to public repo). Package is available to the world. **Still not on our machine.** |
|
|
29
29
|
| **Install** | Put it on our system | `crystal init` or equivalent. Extensions updated. Hooks configured. Only when Parker says "install." |
|
|
30
30
|
|
|
31
|
-
**
|
|
31
|
+
**Prerelease exception:** agents install alpha and beta tracks for validation. Use `ldm install --alpha` after an alpha release and `ldm install --beta` after a beta release. That is test work.
|
|
32
|
+
|
|
33
|
+
**After stable Deploy, STOP.** Do not copy files to `~/.ldm/extensions/` or `~/.openclaw/extensions/`. Do not run `npm install -g`. Do not run `npm link`. Do not run `ldm install` unless Parker explicitly asks. Tell Parker: "v0.X.Y is published. Run the install prompt when you're ready to update."
|
|
32
34
|
|
|
33
35
|
**We always dogfood our own software.** The install prompt exists so Parker can see what's new, review the dry run, and decide to install. If agents deploy directly to extensions, the install prompt says "already up to date" and the dogfooding loop is broken.
|
|
34
36
|
|
|
@@ -31,6 +31,8 @@ ldm install --beta # beta track (npm @beta)
|
|
|
31
31
|
|
|
32
32
|
Each track overwrites whatever is installed. Alpha, beta, and stable all use the same install path. The flag only changes which npm dist-tag is checked for updates.
|
|
33
33
|
|
|
34
|
+
Agents may run alpha and beta installs for prerelease validation. Stable installs are different: Parker dogfoods stable/latest releases through the install prompt unless he explicitly asks an agent to install.
|
|
35
|
+
|
|
34
36
|
## Source Resolution
|
|
35
37
|
|
|
36
38
|
When updating, the installer finds the package in priority order:
|
|
@@ -29,11 +29,13 @@ cd /path/to/repo && git checkout main && git pull
|
|
|
29
29
|
# 4. Alpha release
|
|
30
30
|
wip-release alpha --notes="what changed"
|
|
31
31
|
|
|
32
|
-
#
|
|
32
|
+
# 5. Agent prerelease validation
|
|
33
33
|
ldm install --alpha
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Done. No deploy-public. No public repo sync. The installer pulls from npm @alpha (or finds the local private repo if offline).
|
|
36
|
+
Done. No deploy-public. No public repo sync. The installer pulls from npm @alpha (or finds the local private repo if offline). Agents are expected to run alpha installs when validating prereleases.
|
|
37
|
+
|
|
38
|
+
Beta follows the same rule with `wip-release beta` and `ldm install --beta`. Agents validate alpha and beta tracks. Parker dogfoods stable/latest releases.
|
|
37
39
|
|
|
38
40
|
**Sub-tool versions:** If you changed a sub-tool's code, bump its `package.json` version in the PR. Same version = same code. `wip-release` warns if files changed without a version bump.
|
|
39
41
|
|
|
@@ -53,13 +55,15 @@ wip-release patch # auto-detects release notes
|
|
|
53
55
|
deploy-public /path/to/private-repo org/public-repo
|
|
54
56
|
|
|
55
57
|
# 7. Dogfood
|
|
56
|
-
|
|
58
|
+
Read https://wip.computer/install/wip-ldm-os.txt
|
|
57
59
|
```
|
|
58
60
|
|
|
59
61
|
`wip-release` handles: version bump, CHANGELOG.md, SKILL.md sync, npm publish, GitHub release.
|
|
60
62
|
|
|
61
63
|
`deploy-public` syncs everything except `ai/` to the public repo. Creates a matching release.
|
|
62
64
|
|
|
65
|
+
For stable/latest releases, agents stop after publish and public sync. Do not run `ldm install` or `npm install -g` unless Parker explicitly asks. Parker dogfoods the release through the install prompt.
|
|
66
|
+
|
|
63
67
|
## Quality Gates
|
|
64
68
|
|
|
65
69
|
`wip-release` enforces before publishing (stable only):
|
|
@@ -91,3 +95,5 @@ Same version = same code. Always. If code changed, the version must change. The
|
|
|
91
95
|
| Install | `ldm install [--alpha/--beta]` | Extensions updated on your machine. |
|
|
92
96
|
|
|
93
97
|
For stable releases, add a fourth step: `deploy-public` to sync the public GitHub repo.
|
|
98
|
+
|
|
99
|
+
Ownership rule: agents install alpha and beta for validation. Parker installs stable/latest releases for dogfooding unless he explicitly asks an agent to do it.
|
|
@@ -21,7 +21,9 @@ Then: repo change, PR, merge, release, `ldm install`. That's the only path.
|
|
|
21
21
|
| **Deploy** | wip-release + deploy-public.sh | Published to npm + GitHub. Not on your machine yet. |
|
|
22
22
|
| **Install** | Run the install prompt | Extensions updated on your machine. Only when Parker says "install." |
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
For alpha and beta tracks, agents install prereleases for validation: `ldm install --alpha` or `ldm install --beta`. That is test work, not owner dogfooding.
|
|
25
|
+
|
|
26
|
+
For stable/latest releases, after Deploy, STOP. Do not copy files. Do not npm install -g. Do not npm link. Do not run `ldm install` unless Parker explicitly asks. Parker dogfoods stable releases through the install prompt.
|
|
25
27
|
|
|
26
28
|
## The workflow
|
|
27
29
|
|
|
@@ -31,7 +33,7 @@ After Deploy, STOP. Do not copy files. Do not npm install -g. Do not npm link. D
|
|
|
31
33
|
4. `git checkout main && git pull`
|
|
32
34
|
5. `wip-release patch` (auto-detects release notes)
|
|
33
35
|
6. `deploy-public.sh` to sync public repo
|
|
34
|
-
7.
|
|
36
|
+
7. Stop. Parker dogfoods: `Read https://wip.computer/install/wip-ldm-os.txt`
|
|
35
37
|
|
|
36
38
|
## Never run tools from repo clones
|
|
37
39
|
|