@wipcomputer/wip-ldm-os 0.4.52 → 0.4.54
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 +14 -1
- package/lib/deploy.mjs +125 -52
- package/package.json +1 -1
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.54"
|
|
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
|
@@ -521,6 +521,16 @@ async function cmdInit() {
|
|
|
521
521
|
}
|
|
522
522
|
}
|
|
523
523
|
|
|
524
|
+
// Detect installed harnesses (CC, OC, Codex, Cursor, Claude macOS)
|
|
525
|
+
try {
|
|
526
|
+
const { detectHarnesses } = await import('../lib/deploy.mjs');
|
|
527
|
+
const { harnesses } = detectHarnesses();
|
|
528
|
+
const detected = Object.entries(harnesses).filter(([,h]) => h.detected).map(([name]) => name);
|
|
529
|
+
if (detected.length > 0) {
|
|
530
|
+
console.log(` + Harnesses detected: ${detected.join(', ')}`);
|
|
531
|
+
}
|
|
532
|
+
} catch {}
|
|
533
|
+
|
|
524
534
|
console.log('');
|
|
525
535
|
console.log(` LDM OS v${PKG_VERSION} initialized at ${LDM_ROOT}`);
|
|
526
536
|
console.log('');
|
|
@@ -627,9 +637,12 @@ async function cmdInstall() {
|
|
|
627
637
|
cmdInit();
|
|
628
638
|
}
|
|
629
639
|
|
|
630
|
-
const { setFlags, installFromPath, installSingleTool, installToolbox } = await import('../lib/deploy.mjs');
|
|
640
|
+
const { setFlags, installFromPath, installSingleTool, installToolbox, detectHarnesses } = await import('../lib/deploy.mjs');
|
|
631
641
|
const { detectInterfacesJSON } = await import('../lib/detect.mjs');
|
|
632
642
|
|
|
643
|
+
// Refresh harness detection (catches newly installed harnesses)
|
|
644
|
+
detectHarnesses();
|
|
645
|
+
|
|
633
646
|
setFlags({ dryRun: DRY_RUN, jsonOutput: JSON_OUTPUT });
|
|
634
647
|
|
|
635
648
|
// --help flag (#81)
|
package/lib/deploy.mjs
CHANGED
|
@@ -76,9 +76,91 @@ function saveRegistry(registry) {
|
|
|
76
76
|
writeJSON(REGISTRY_PATH, registry);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// Core extensions are always enabled.
|
|
79
|
+
// Core extensions are always enabled.
|
|
80
80
|
const CORE_EXTENSIONS = new Set(['memory-crystal']);
|
|
81
81
|
|
|
82
|
+
// ── Harness Detection ──
|
|
83
|
+
|
|
84
|
+
const LDM_CONFIG_PATH = join(LDM_ROOT, 'config.json');
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Detect which AI harnesses are installed on this system.
|
|
88
|
+
* Writes results to ~/.ldm/config.json so the installer knows where to deploy.
|
|
89
|
+
*/
|
|
90
|
+
export function detectHarnesses() {
|
|
91
|
+
const harnesses = {};
|
|
92
|
+
|
|
93
|
+
// Claude Code CLI
|
|
94
|
+
const claudeHome = join(HOME, '.claude');
|
|
95
|
+
harnesses['claude-code'] = {
|
|
96
|
+
detected: existsSync(claudeHome),
|
|
97
|
+
home: claudeHome,
|
|
98
|
+
skills: join(claudeHome, 'skills'),
|
|
99
|
+
rules: join(claudeHome, 'rules'),
|
|
100
|
+
settings: join(claudeHome, 'settings.json'),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Claude macOS app
|
|
104
|
+
const claudeMacHome = join(HOME, 'Library', 'Application Support', 'Claude');
|
|
105
|
+
harnesses['claude-macos'] = {
|
|
106
|
+
detected: existsSync(claudeMacHome),
|
|
107
|
+
home: claudeMacHome,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// OpenClaw (Lesa)
|
|
111
|
+
harnesses['openclaw'] = {
|
|
112
|
+
detected: existsSync(OC_ROOT),
|
|
113
|
+
home: OC_ROOT,
|
|
114
|
+
skills: join(OC_ROOT, 'skills'),
|
|
115
|
+
extensions: OC_EXTENSIONS,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Codex
|
|
119
|
+
const codexHome = join(HOME, '.codex');
|
|
120
|
+
harnesses['codex'] = {
|
|
121
|
+
detected: existsSync(codexHome),
|
|
122
|
+
home: codexHome,
|
|
123
|
+
skills: join(codexHome, 'skills'),
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Cursor
|
|
127
|
+
const cursorHome = join(HOME, '.cursor');
|
|
128
|
+
harnesses['cursor'] = {
|
|
129
|
+
detected: existsSync(cursorHome),
|
|
130
|
+
home: cursorHome,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Read workspace from existing config
|
|
134
|
+
let workspace = '';
|
|
135
|
+
try {
|
|
136
|
+
const existing = readJSON(LDM_CONFIG_PATH) || {};
|
|
137
|
+
workspace = (existing.workspace || '').replace('~', HOME);
|
|
138
|
+
} catch {}
|
|
139
|
+
|
|
140
|
+
// Save to config
|
|
141
|
+
try {
|
|
142
|
+
const existing = readJSON(LDM_CONFIG_PATH) || {};
|
|
143
|
+
existing.harnesses = harnesses;
|
|
144
|
+
writeJSON(LDM_CONFIG_PATH, existing);
|
|
145
|
+
} catch {}
|
|
146
|
+
|
|
147
|
+
return { harnesses, workspace };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get detected harnesses from config. Runs detection if not cached.
|
|
152
|
+
*/
|
|
153
|
+
function getHarnesses() {
|
|
154
|
+
try {
|
|
155
|
+
const config = readJSON(LDM_CONFIG_PATH) || {};
|
|
156
|
+
if (config.harnesses) {
|
|
157
|
+
const workspace = (config.workspace || '').replace('~', HOME);
|
|
158
|
+
return { harnesses: config.harnesses, workspace };
|
|
159
|
+
}
|
|
160
|
+
} catch {}
|
|
161
|
+
return detectHarnesses();
|
|
162
|
+
}
|
|
163
|
+
|
|
82
164
|
function updateRegistry(name, info) {
|
|
83
165
|
const registry = loadRegistry();
|
|
84
166
|
const existing = registry.extensions[name];
|
|
@@ -86,7 +168,7 @@ function updateRegistry(name, info) {
|
|
|
86
168
|
registry.extensions[name] = {
|
|
87
169
|
...existing,
|
|
88
170
|
...info,
|
|
89
|
-
enabled: existing?.enabled ??
|
|
171
|
+
enabled: existing?.enabled ?? true, // New installs are enabled by default. User runs ldm disable to turn off.
|
|
90
172
|
updatedAt: new Date().toISOString(),
|
|
91
173
|
};
|
|
92
174
|
saveRegistry(registry);
|
|
@@ -650,66 +732,57 @@ function installClaudeCodeHook(repoPath, door) {
|
|
|
650
732
|
}
|
|
651
733
|
|
|
652
734
|
function installSkill(repoPath, toolName) {
|
|
653
|
-
const
|
|
654
|
-
const ocSkillDir = join(OC_ROOT, 'skills', toolName);
|
|
655
|
-
const ocSkillDest = join(ocSkillDir, 'SKILL.md');
|
|
735
|
+
const { harnesses, workspace } = getHarnesses();
|
|
656
736
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
737
|
+
// Find SKILL.md source: repo path first, then permanent copy at ~/.ldm/extensions/
|
|
738
|
+
let skillSrc = join(repoPath, 'SKILL.md');
|
|
739
|
+
const permanentSkill = join(LDM_EXTENSIONS, toolName, 'SKILL.md');
|
|
740
|
+
if (!existsSync(skillSrc) && existsSync(permanentSkill)) skillSrc = permanentSkill;
|
|
741
|
+
if (!existsSync(skillSrc)) return false;
|
|
742
|
+
|
|
743
|
+
// Find references/ source: repo path first, then permanent copy
|
|
744
|
+
let refsSrc = join(repoPath, 'references');
|
|
745
|
+
const permanentRefs = join(LDM_EXTENSIONS, toolName, 'references');
|
|
746
|
+
if (!existsSync(refsSrc) && existsSync(permanentRefs)) refsSrc = permanentRefs;
|
|
667
747
|
|
|
668
748
|
if (DRY_RUN) {
|
|
669
|
-
|
|
749
|
+
const targets = Object.entries(harnesses).filter(([,h]) => h.detected && h.skills).map(([name]) => name);
|
|
750
|
+
ok(`Skill: would deploy ${toolName} to ${targets.join(', ')} (dry run)`);
|
|
670
751
|
return true;
|
|
671
752
|
}
|
|
672
753
|
|
|
673
754
|
try {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
if (existsSync(
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
// To Claude Code skill dir
|
|
693
|
-
if (existsSync(ccSkillDir)) {
|
|
694
|
-
cpSync(refsSrc, join(ccSkillDir, 'references'), { recursive: true });
|
|
755
|
+
const deployed = [];
|
|
756
|
+
|
|
757
|
+
// 1. Save permanent copy to ~/.ldm/extensions/<name>/ (survives tmp cleanup)
|
|
758
|
+
const ldmSkillDir = join(LDM_EXTENSIONS, toolName);
|
|
759
|
+
mkdirSync(ldmSkillDir, { recursive: true });
|
|
760
|
+
cpSync(skillSrc, join(ldmSkillDir, 'SKILL.md'));
|
|
761
|
+
if (existsSync(refsSrc) && refsSrc !== permanentRefs) {
|
|
762
|
+
cpSync(refsSrc, join(ldmSkillDir, 'references'), { recursive: true });
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// 2. Deploy to every detected harness that has a skills path
|
|
766
|
+
for (const [name, harness] of Object.entries(harnesses)) {
|
|
767
|
+
if (!harness.detected || !harness.skills) continue;
|
|
768
|
+
const dest = join(harness.skills, toolName);
|
|
769
|
+
mkdirSync(dest, { recursive: true });
|
|
770
|
+
cpSync(skillSrc, join(dest, 'SKILL.md'));
|
|
771
|
+
if (existsSync(refsSrc)) {
|
|
772
|
+
cpSync(refsSrc, join(dest, 'references'), { recursive: true });
|
|
695
773
|
}
|
|
774
|
+
deployed.push(name);
|
|
775
|
+
}
|
|
696
776
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
if (workspace && existsSync(workspace)) {
|
|
704
|
-
const homeRefsDest = join(workspace, 'settings', 'docs', 'skills', toolName);
|
|
705
|
-
mkdirSync(homeRefsDest, { recursive: true });
|
|
706
|
-
cpSync(refsSrc, homeRefsDest, { recursive: true });
|
|
707
|
-
ok(`Skill: references/ deployed to ${homeRefsDest.replace(HOME, '~')}`);
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
} catch {}
|
|
777
|
+
// 3. Deploy references/ to home (workspace settings/docs/skills/)
|
|
778
|
+
if (existsSync(refsSrc) && workspace && existsSync(workspace)) {
|
|
779
|
+
const homeRefsDest = join(workspace, 'settings', 'docs', 'skills', toolName);
|
|
780
|
+
mkdirSync(homeRefsDest, { recursive: true });
|
|
781
|
+
cpSync(refsSrc, homeRefsDest, { recursive: true });
|
|
782
|
+
deployed.push('home');
|
|
711
783
|
}
|
|
712
784
|
|
|
785
|
+
ok(`Skill: ${toolName} deployed to ${deployed.join(', ')}`);
|
|
713
786
|
return true;
|
|
714
787
|
} catch (e) {
|
|
715
788
|
fail(`Skill: deploy failed. ${e.message}`);
|
|
@@ -1016,4 +1089,4 @@ export function disableExtension(name) {
|
|
|
1016
1089
|
|
|
1017
1090
|
// ── Exports for ldm CLI ──
|
|
1018
1091
|
|
|
1019
|
-
export { loadRegistry, saveRegistry, updateRegistry, readJSON, writeJSON, runBuildIfNeeded, CORE_EXTENSIONS };
|
|
1092
|
+
export { loadRegistry, saveRegistry, updateRegistry, readJSON, writeJSON, runBuildIfNeeded, detectHarnesses, CORE_EXTENSIONS };
|