@wipcomputer/wip-ldm-os 0.4.15 → 0.4.16

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 CHANGED
@@ -5,7 +5,7 @@ license: MIT
5
5
  interface: [cli, skill]
6
6
  metadata:
7
7
  display-name: "LDM OS"
8
- version: "0.4.15"
8
+ version: "0.4.16"
9
9
  homepage: "https://github.com/wipcomputer/wip-ldm-os"
10
10
  author: "Parker Todd Brooks"
11
11
  category: infrastructure
package/bin/ldm.js CHANGED
@@ -447,6 +447,23 @@ async function cmdInstall() {
447
447
 
448
448
  setFlags({ dryRun: DRY_RUN, jsonOutput: JSON_OUTPUT });
449
449
 
450
+ // --help flag (#81)
451
+ if (args.includes('--help') || args.includes('-h')) {
452
+ console.log(`
453
+ ldm install Update all registered extensions + CLIs
454
+ ldm install <org/repo> Install from GitHub
455
+ ldm install <npm-package> Install from npm
456
+ ldm install <path> Install from local directory
457
+
458
+ Flags:
459
+ --dry-run Show what would change, don't install
460
+ --json JSON output
461
+ --yes Auto-accept catalog prompts
462
+ --none Skip catalog prompts
463
+ `);
464
+ process.exit(0);
465
+ }
466
+
450
467
  // Find the target (skip flags)
451
468
  const target = args.slice(1).find(a => !a.startsWith('--'));
452
469
 
@@ -493,6 +510,11 @@ async function cmdInstall() {
493
510
  }
494
511
 
495
512
  await installFromPath(repoPath);
513
+
514
+ // Clean up /tmp/ clone after install (#32)
515
+ if (!DRY_RUN && (repoPath.startsWith('/tmp/') || repoPath.startsWith('/private/tmp/'))) {
516
+ try { execSync(`rm -rf "${repoPath}"`, { stdio: 'pipe' }); } catch {}
517
+ }
496
518
  return;
497
519
  }
498
520
 
@@ -570,6 +592,11 @@ async function cmdInstall() {
570
592
  }
571
593
 
572
594
  await installFromPath(repoPath);
595
+
596
+ // Clean up /tmp/ clone after install (#32)
597
+ if (!DRY_RUN && (repoPath.startsWith('/tmp/') || repoPath.startsWith('/private/tmp/'))) {
598
+ try { execSync(`rm -rf "${repoPath}"`, { stdio: 'pipe' }); } catch {}
599
+ }
573
600
  }
574
601
 
575
602
  // ── Auto-detect unregistered extensions ──
@@ -689,6 +716,16 @@ async function cmdInstallCatalog() {
689
716
  return matches.includes(name) || c.id === name;
690
717
  });
691
718
 
719
+ // Fallback: use repository.url from extension's package.json (#82)
720
+ let repoUrl = catalogEntry?.repo || null;
721
+ if (!repoUrl && extPkg?.repository) {
722
+ const raw = typeof extPkg.repository === 'string'
723
+ ? extPkg.repository
724
+ : extPkg.repository.url || '';
725
+ const ghMatch = raw.match(/github\.com[:/]([^/]+\/[^/.]+)/);
726
+ if (ghMatch) repoUrl = ghMatch[1];
727
+ }
728
+
692
729
  const currentVersion = entry.ldmVersion || entry.ocVersion;
693
730
  if (!currentVersion) continue;
694
731
 
@@ -700,7 +737,7 @@ async function cmdInstallCatalog() {
700
737
  if (latestVersion && latestVersion !== currentVersion) {
701
738
  npmUpdates.push({
702
739
  ...entry,
703
- catalogRepo: catalogEntry?.repo || null,
740
+ catalogRepo: repoUrl,
704
741
  catalogNpm: npmPkg,
705
742
  currentVersion,
706
743
  latestVersion,
@@ -710,6 +747,39 @@ async function cmdInstallCatalog() {
710
747
  } catch {}
711
748
  }
712
749
 
750
+ // Check global CLIs not tracked by extension loop (#81)
751
+ for (const [binName, binInfo] of Object.entries(state.cliBinaries || {})) {
752
+ const catalogComp = components.find(c =>
753
+ (c.cliMatches || []).includes(binName)
754
+ );
755
+ if (!catalogComp || !catalogComp.npm) continue;
756
+ // Skip if already covered by extension loop
757
+ if (npmUpdates.some(e =>
758
+ e.catalogNpm === catalogComp.npm ||
759
+ (catalogComp.registryMatches || []).includes(e.name)
760
+ )) continue;
761
+
762
+ const currentVersion = binInfo.version;
763
+ if (!currentVersion) continue;
764
+
765
+ try {
766
+ const latestVersion = execSync(`npm view ${catalogComp.npm} version 2>/dev/null`, {
767
+ encoding: 'utf8', timeout: 10000,
768
+ }).trim();
769
+ if (latestVersion && latestVersion !== currentVersion) {
770
+ npmUpdates.push({
771
+ name: binName,
772
+ catalogRepo: catalogComp.repo,
773
+ catalogNpm: catalogComp.npm,
774
+ currentVersion,
775
+ latestVersion,
776
+ hasUpdate: true,
777
+ cliOnly: true,
778
+ });
779
+ }
780
+ } catch {}
781
+ }
782
+
713
783
  const totalUpdates = npmUpdates.length;
714
784
 
715
785
  if (DRY_RUN) {
@@ -802,8 +872,20 @@ async function cmdInstallCatalog() {
802
872
 
803
873
  let updated = 0;
804
874
 
805
- // Update from npm via catalog repos (#55)
875
+ // Update from npm via catalog repos (#55) and CLIs (#81)
806
876
  for (const entry of npmUpdates) {
877
+ // CLI-only entries: install directly from npm (#81)
878
+ if (entry.cliOnly) {
879
+ console.log(` Updating CLI ${entry.name} v${entry.currentVersion} -> v${entry.latestVersion}...`);
880
+ try {
881
+ execSync(`npm install -g ${entry.catalogNpm}@${entry.latestVersion}`, { stdio: 'inherit' });
882
+ updated++;
883
+ } catch (e) {
884
+ console.error(` x Failed to update CLI ${entry.name}: ${e.message}`);
885
+ }
886
+ continue;
887
+ }
888
+
807
889
  if (!entry.catalogRepo) {
808
890
  console.log(` Skipping ${entry.name}: no catalog repo (install manually with ldm install <org/repo>)`);
809
891
  continue;
package/catalog.json CHANGED
@@ -53,8 +53,8 @@
53
53
  "description": "Release pipeline, license compliance, repo management, identity file protection.",
54
54
  "npm": "@wipcomputer/universal-installer",
55
55
  "repo": "wipcomputer/wip-ai-devops-toolbox",
56
- "registryMatches": ["wip-repos", "wip-release", "wip-file-guard", "wip-license-hook", "wip-repo-permissions-hook", "universal-installer", "deploy-public", "post-merge-rename", "wip-license-guard", "wip-repo-init", "wip-readme-format"],
57
- "cliMatches": ["wip-release", "wip-repos", "wip-file-guard", "wip-install"],
56
+ "registryMatches": ["wip-repos", "wip-release", "wip-file-guard", "wip-license-hook", "wip-repo-permissions-hook", "universal-installer", "deploy-public", "post-merge-rename", "wip-license-guard", "wip-repo-init", "wip-readme-format", "wip-branch-guard"],
57
+ "cliMatches": ["wip-release", "wip-repos", "wip-file-guard", "wip-install", "wip-branch-guard"],
58
58
  "recommended": false,
59
59
  "status": "stable",
60
60
  "postInstall": null,
package/lib/deploy.mjs CHANGED
@@ -372,11 +372,25 @@ function installCLI(repoPath, door) {
372
372
  return true;
373
373
  }
374
374
  } catch {
375
- // Registry check failed, fall through to local install
375
+ // Registry check failed, fall through
376
376
  }
377
+
378
+ // Exact version not on npm. Try latest from registry instead of local install (#32, #81)
379
+ try {
380
+ const latestVersion = execSync(`npm view ${packageName} version 2>/dev/null`, {
381
+ encoding: 'utf8', timeout: 15000,
382
+ }).trim();
383
+ if (latestVersion) {
384
+ execSync(`npm install -g ${packageName}@${latestVersion}`, { stdio: 'pipe', timeout: 60000 });
385
+ ensureBinExecutable(binNames);
386
+ ok(`CLI: ${binNames.join(', ')} installed from registry (v${latestVersion}, repo has v${packageVersion})`);
387
+ return true;
388
+ }
389
+ } catch {}
377
390
  }
378
391
 
379
- // Fallback: local install (creates symlinks, but better than nothing)
392
+ // Last resort: local install (creates symlinks ... warns user)
393
+ console.log(` ! Warning: installing locally from ${repoPath} (creates symlinks to source dir)`);
380
394
  try {
381
395
  execSync('npm install -g .', { cwd: repoPath, stdio: 'pipe' });
382
396
  ensureBinExecutable(binNames);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ldm-os",
3
- "version": "0.4.15",
3
+ "version": "0.4.16",
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",