@wipcomputer/wip-ldm-os 0.2.9 → 0.2.11

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/LICENSE.md ADDED
@@ -0,0 +1,52 @@
1
+ Dual License: MIT + AGPLv3
2
+
3
+ Copyright (c) 2026 WIP Computer, Inc.
4
+
5
+
6
+ 1. MIT License (local and personal use)
7
+ ---------------------------------------
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+
28
+ 2. GNU Affero General Public License v3.0 (commercial and cloud use)
29
+ --------------------------------------------------------------------
30
+
31
+ If you run this software as part of a hosted service, cloud platform,
32
+ marketplace listing, or any network-accessible offering for commercial
33
+ purposes, the AGPLv3 terms apply. You must either:
34
+
35
+ a) Release your complete source code under AGPLv3, or
36
+ b) Obtain a commercial license.
37
+
38
+ This program is free software: you can redistribute it and/or modify
39
+ it under the terms of the GNU Affero General Public License as published
40
+ by the Free Software Foundation, either version 3 of the License, or
41
+ (at your option) any later version.
42
+
43
+ This program is distributed in the hope that it will be useful,
44
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
45
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46
+ GNU Affero General Public License for more details.
47
+
48
+ You should have received a copy of the GNU Affero General Public License
49
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
50
+
51
+
52
+ AGPLv3 for personal use is free. Commercial licenses available.
package/README.md CHANGED
@@ -20,23 +20,25 @@ LDM OS is a shared infrastructure that enables:
20
20
  Open your AI and paste this:
21
21
 
22
22
  ```
23
- Read the SKILL.md at github.com/wipcomputer/wip-ldm-os/blob/main/SKILL.md.
23
+ Read wip.computer/install/ldm-os.txt
24
24
 
25
- Then explain to me:
26
- 1. What are these tools?
27
- 2. What do they do?
28
- 3. What would change about how we work together?
29
- 4. What would it change about how all my AI work?
25
+ Then explain:
26
+ 1. What is LDM OS?
27
+ 2. What does it install on my system?
28
+ 3. What changes for us? (this AI)
29
+ 4. What changes across all my AIs?
30
30
 
31
- Then ask me:
32
- - Do you have more questions?
33
- - Do you want to install it?
31
+ Check if LDM OS is already installed.
34
32
 
35
- If I say yes, run: ldm install --dry-run
33
+ If it is, show me what I have and what's new.
34
+
35
+ Then ask:
36
+ - Do you have questions?
37
+ - Want to see a dry run?
36
38
 
37
- And show me exactly what will change on my system.
39
+ If I say yes, run: ldm install --dry-run
38
40
 
39
- When I'm ready, I'll tell you to install for real.
41
+ Show me exactly what will change. Don't install anything until I say "install".
40
42
  ```
41
43
 
42
44
  That's it. Your AI reads the spec, explains what it does, and walks you through a dry run before touching anything.
@@ -65,7 +67,7 @@ Ships with LDM OS.
65
67
 
66
68
  **LUME**
67
69
  - Language for Unified Memory and Emergence. A memory language for AI agents to document their own learning and maintain continuity across sessions. Not a programming language. A way for your AI to write memories to itself, retrieve past learnings, track unfinished thoughts, and pass context between sessions.
68
- - [Read more about LUME](https://wipcomputer.github.io/wip-homepage/)
70
+ - [Read more about LUME](https://wip.computer/lume/)
69
71
 
70
72
  ## Optional Skills
71
73
 
@@ -124,7 +126,7 @@ AGPLv3 for personal use is free. Commercial licenses available.
124
126
 
125
127
  **Need a commercial license:**
126
128
  - Bundle into a product you sell
127
- - List on a marketplace (VS Code, JetBrains, etc.)
129
+ - List on a marketplace (Claude Marketplace, OAI GPT/Apps, Clawhub.ai, VS Code, etc.)
128
130
  - Offer as part of a hosted/SaaS platform
129
131
  - Redistribute commercially
130
132
 
@@ -132,8 +134,8 @@ Using these tools to build your own software is fine. Reselling the tools themse
132
134
 
133
135
  By submitting a PR, you agree to the [Contributor License Agreement](CLA.md).
134
136
 
135
- Built by Parker Todd Brooks, Lēsa (OpenClaw, Claude Opus 4.6), Claude Code (Claude Opus 4.6).
136
-
137
137
  ---
138
138
 
139
+ Built by Parker Todd Brooks, Lēsa (OpenClaw, Claude Opus 4.6), Claude Code (Claude Opus 4.6), GPT 5.x, Grok 4.20).
140
+
139
141
  *WIP.computer. Learning Dreaming Machines.*
package/SKILL.md CHANGED
@@ -5,7 +5,7 @@ license: MIT
5
5
  interface: [cli, skill]
6
6
  metadata:
7
7
  display-name: "LDM OS"
8
- version: "0.2.9"
8
+ version: "0.2.11".2.10"
9
9
  homepage: "https://github.com/wipcomputer/wip-ldm-os"
10
10
  author: "Parker Todd Brooks"
11
11
  category: infrastructure
@@ -174,3 +174,13 @@ LDM OS is the runtime. Skills plug into it:
174
174
  - **Bridge** ... `wipcomputer/wip-bridge`
175
175
 
176
176
  Run `ldm install` anytime to add more skills.
177
+
178
+ ## Claude Code Marketplace
179
+
180
+ If you're running Claude Code, you can browse and install all LDM OS plugins available from WIP Computer:
181
+
182
+ ```
183
+ /plugin marketplace add wipcomputer/claude-plugins
184
+ ```
185
+
186
+ This adds LDM OS skills to Claude Code's Discover tab alongside Anthropic's official plugins. Install any skill with `/plugin install`.
package/bin/ldm.js CHANGED
@@ -82,7 +82,6 @@ async function cmdInit() {
82
82
  join(LDM_ROOT, 'agents'),
83
83
  join(LDM_ROOT, 'memory'),
84
84
  join(LDM_ROOT, 'state'),
85
- join(LDM_ROOT, 'secrets'),
86
85
  join(LDM_ROOT, 'shared', 'boot'),
87
86
  ];
88
87
 
@@ -300,10 +299,37 @@ async function cmdInstall() {
300
299
  return;
301
300
  }
302
301
 
303
- // Resolve target: GitHub URL, org/repo shorthand, or local path
302
+ // Resolve target: npm package, GitHub URL, org/repo shorthand, or local path
304
303
  let repoPath;
305
304
 
306
- if (target.startsWith('http') || target.startsWith('git@') || target.match(/^[\w-]+\/[\w.-]+$/)) {
305
+ // Check if target looks like an npm package (starts with @ or is a plain name without /)
306
+ if (target.startsWith('@') || (!target.includes('/') && !existsSync(resolve(target)))) {
307
+ // Try npm install to temp dir
308
+ const npmName = target;
309
+ const tempDir = join('/tmp', `ldm-install-npm-${Date.now()}`);
310
+ console.log('');
311
+ console.log(` Installing ${npmName} from npm...`);
312
+ try {
313
+ mkdirSync(tempDir, { recursive: true });
314
+ execSync(`npm install ${npmName} --prefix "${tempDir}"`, { stdio: 'pipe' });
315
+ // Find the installed package in node_modules
316
+ const pkgName = npmName.startsWith('@') ? npmName : npmName;
317
+ const installed = join(tempDir, 'node_modules', pkgName);
318
+ if (existsSync(installed)) {
319
+ console.log(` + Installed from npm`);
320
+ repoPath = installed;
321
+ } else {
322
+ console.error(` x Package installed but not found at expected path`);
323
+ process.exit(1);
324
+ }
325
+ } catch (e) {
326
+ // npm failed, fall through to git clone or path resolution
327
+ console.log(` npm install failed, trying other methods...`);
328
+ try { execSync(`rm -rf "${tempDir}"`, { stdio: 'pipe' }); } catch {}
329
+ }
330
+ }
331
+
332
+ if (!repoPath && (target.startsWith('http') || target.startsWith('git@') || target.match(/^[\w-]+\/[\w.-]+$/))) {
307
333
  const isShorthand = target.match(/^[\w-]+\/[\w.-]+$/);
308
334
  const httpsUrl = isShorthand
309
335
  ? `https://github.com/${target}.git`
@@ -332,7 +358,7 @@ async function cmdInstall() {
332
358
  console.error(` x Clone failed: ${e.message}`);
333
359
  process.exit(1);
334
360
  }
335
- } else {
361
+ } else if (!repoPath) {
336
362
  repoPath = resolve(target);
337
363
  if (!existsSync(repoPath)) {
338
364
  console.error(` x Path not found: ${repoPath}`);
package/lib/deploy.mjs CHANGED
@@ -159,8 +159,34 @@ function runBuildIfNeeded(repoPath) {
159
159
  }
160
160
  }
161
161
 
162
+ // ── Version comparison (fix #7) ──
163
+
164
+ function compareSemver(a, b) {
165
+ if (!a || !b) return 0;
166
+ const pa = a.split('.').map(Number);
167
+ const pb = b.split('.').map(Number);
168
+ for (let i = 0; i < 3; i++) {
169
+ const na = pa[i] || 0;
170
+ const nb = pb[i] || 0;
171
+ if (na > nb) return 1;
172
+ if (na < nb) return -1;
173
+ }
174
+ return 0;
175
+ }
176
+
177
+ // Config files that should never be overwritten during updates
178
+ const PRESERVE_PATTERNS = [
179
+ 'boot-config.json', '.env', '.env.local',
180
+ 'config.local.json', 'settings.local.json',
181
+ ];
182
+
183
+ function isPreservedFile(filename) {
184
+ return PRESERVE_PATTERNS.some(p => filename === p || filename.endsWith('.local'));
185
+ }
186
+
162
187
  // ── Safe deploy (fix #7) ──
163
188
  // Deploy to temp dir first, then atomic rename. Never rm -rf the live dir.
189
+ // Preserves config files from existing installs.
164
190
 
165
191
  function copyFiltered(src, dest) {
166
192
  cpSync(src, dest, {
@@ -169,6 +195,23 @@ function copyFiltered(src, dest) {
169
195
  });
170
196
  }
171
197
 
198
+ function restorePreservedFiles(oldDir, newDir) {
199
+ if (!existsSync(oldDir)) return;
200
+ try {
201
+ const entries = readdirSync(oldDir);
202
+ for (const entry of entries) {
203
+ if (isPreservedFile(entry)) {
204
+ const oldPath = join(oldDir, entry);
205
+ const newPath = join(newDir, entry);
206
+ if (existsSync(oldPath) && !existsSync(newPath)) {
207
+ cpSync(oldPath, newPath);
208
+ ok(`Preserved config: ${entry}`);
209
+ }
210
+ }
211
+ }
212
+ } catch {}
213
+ }
214
+
172
215
  function safeDeployDir(repoPath, destDir, name) {
173
216
  const finalPath = join(destDir, name);
174
217
  const tempPath = join(tmpdir(), `ldm-deploy-${name}-${Date.now()}`);
@@ -200,7 +243,12 @@ function safeDeployDir(repoPath, destDir, name) {
200
243
  }
201
244
  renameSync(tempPath, finalPath);
202
245
 
203
- // 5. Trash the old version (never delete)
246
+ // 5. Restore preserved config files from old version
247
+ if (existsSync(backupPath)) {
248
+ restorePreservedFiles(backupPath, finalPath);
249
+ }
250
+
251
+ // 6. Trash the old version (never delete)
204
252
  if (existsSync(backupPath)) {
205
253
  const trashed = moveToTrash(backupPath);
206
254
  if (trashed) ok(`Old version moved to ${trashed}`);
@@ -337,8 +385,9 @@ function deployExtension(repoPath, name) {
337
385
  const newVersion = sourcePkg?.version;
338
386
  const currentVersion = installedPkg?.version;
339
387
 
340
- if (newVersion && currentVersion && newVersion === currentVersion) {
341
- skip(`LDM: ${name} already at v${currentVersion}`);
388
+ const cmp = compareSemver(newVersion, currentVersion);
389
+ if (newVersion && currentVersion && cmp <= 0) {
390
+ skip(`LDM: ${name} already at v${currentVersion}${cmp < 0 ? ` (source is older: v${newVersion})` : ''}`);
342
391
  // Ensure OC copy exists too
343
392
  const ocName = resolveOcPluginName(repoPath, name);
344
393
  const ocDest = join(OC_EXTENSIONS, ocName);
@@ -382,11 +431,35 @@ function deployExtension(repoPath, name) {
382
431
  fail(`OpenClaw: deploy failed for ${ocName}`);
383
432
  } else {
384
433
  ok(`OpenClaw: deployed to ${join(OC_EXTENSIONS, ocName)}`);
434
+ // Verify openclaw.json references match actual directory
435
+ verifyOcConfig(ocName);
385
436
  }
386
437
 
387
438
  return true;
388
439
  }
389
440
 
441
+ function verifyOcConfig(pluginDirName) {
442
+ const ocConfigPath = join(OC_ROOT, 'openclaw.json');
443
+ const ocConfig = readJSON(ocConfigPath);
444
+ if (!ocConfig?.extensions) return;
445
+
446
+ const pluginPath = join(OC_EXTENSIONS, pluginDirName);
447
+ const pluginJson = readJSON(join(pluginPath, 'openclaw.plugin.json'));
448
+ if (!pluginJson?.id) return;
449
+
450
+ // Check if openclaw.json has an entry whose path references a different dir
451
+ for (const ext of ocConfig.extensions) {
452
+ if (ext.id === pluginJson.id) {
453
+ const configDir = basename(ext.path || '');
454
+ if (configDir && configDir !== pluginDirName) {
455
+ log(`Warning: openclaw.json references "${configDir}" but plugin is at "${pluginDirName}"`);
456
+ log(` Update openclaw.json or rename the directory to match.`);
457
+ }
458
+ return;
459
+ }
460
+ }
461
+ }
462
+
390
463
  function registerMCP(repoPath, door, toolName) {
391
464
  const rawName = toolName || door.name || basename(repoPath);
392
465
  const name = rawName.replace(/^@[\w-]+\//, '');
package/lib/detect.mjs CHANGED
@@ -65,6 +65,12 @@ export function detectInterfaces(repoPath) {
65
65
  };
66
66
  }
67
67
 
68
+ // 7. Claude Code Plugin: .claude-plugin/plugin.json
69
+ const ccPluginManifest = join(repoPath, '.claude-plugin', 'plugin.json');
70
+ if (existsSync(ccPluginManifest)) {
71
+ interfaces.claudeCodePlugin = { manifest: readJSON(ccPluginManifest), path: ccPluginManifest };
72
+ }
73
+
68
74
  return { interfaces, pkg };
69
75
  }
70
76
 
@@ -88,6 +94,7 @@ export function describeInterfaces(interfaces) {
88
94
  if (interfaces.openclaw) lines.push(`OpenClaw Plugin: ${interfaces.openclaw.config?.name || 'detected'}`);
89
95
  if (interfaces.skill) lines.push(`Skill: SKILL.md`);
90
96
  if (interfaces.claudeCodeHook) lines.push(`Claude Code Hook: ${interfaces.claudeCodeHook.event || 'PreToolUse'}`);
97
+ if (interfaces.claudeCodePlugin) lines.push(`Claude Code Plugin: ${interfaces.claudeCodePlugin.manifest?.name || 'detected'}`);
91
98
 
92
99
  return `${names.length} interface(s): ${names.join(', ')}\n${lines.map(l => ` ${l}`).join('\n')}`;
93
100
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ldm-os",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "type": "module",
5
5
  "description": "LDM OS: identity, memory, and sovereignty infrastructure for AI agents",
6
6
  "main": "src/boot/boot-hook.mjs",