@wipcomputer/wip-ldm-os 0.4.75-alpha.1 → 0.4.76
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 +51 -17
- 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.76"
|
|
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
|
@@ -940,7 +940,16 @@ function installClaudeCodeHookEvent(repoPath, door) {
|
|
|
940
940
|
// Deploy guard.mjs to ~/.ldm/extensions/{toolName}/ (#85: always update, not just when missing)
|
|
941
941
|
// Idempotent across multi-door invocations: two doors on the same repo
|
|
942
942
|
// will both trigger this copy, which is a filesystem no-op after the first.
|
|
943
|
+
//
|
|
944
|
+
// Also recursively copy sibling source subdirectories (e.g. lib/, dist/).
|
|
945
|
+
// Historical behavior only copied guard.mjs + package.json at the root,
|
|
946
|
+
// so a guard.mjs whose imports referenced ./lib/*.mjs loaded fine from
|
|
947
|
+
// source but broke post-install with ERR_MODULE_NOT_FOUND. This caused
|
|
948
|
+
// the wip-branch-guard 1.9.77 incident on 2026-04-20.
|
|
943
949
|
const srcGuard = join(repoPath, 'guard.mjs');
|
|
950
|
+
const SKIP_DIRS_FOR_HOOK = new Set([
|
|
951
|
+
'.git', 'node_modules', 'ai', '_trash', '.worktrees', 'logs', 'test', 'tests', '__tests__',
|
|
952
|
+
]);
|
|
944
953
|
if (existsSync(srcGuard)) {
|
|
945
954
|
try {
|
|
946
955
|
if (!existsSync(extDir)) mkdirSync(extDir, { recursive: true });
|
|
@@ -948,6 +957,14 @@ function installClaudeCodeHookEvent(repoPath, door) {
|
|
|
948
957
|
// Also copy package.json for metadata
|
|
949
958
|
const srcPkg = join(repoPath, 'package.json');
|
|
950
959
|
if (existsSync(srcPkg)) copyFileSync(srcPkg, join(extDir, 'package.json'));
|
|
960
|
+
// Recurse sibling subdirs so nested imports (e.g. ./lib/foo.mjs) load.
|
|
961
|
+
for (const entry of readdirSync(repoPath, { withFileTypes: true })) {
|
|
962
|
+
if (!entry.isDirectory()) continue;
|
|
963
|
+
if (SKIP_DIRS_FOR_HOOK.has(entry.name)) continue;
|
|
964
|
+
const srcDir = join(repoPath, entry.name);
|
|
965
|
+
const destDir = join(extDir, entry.name);
|
|
966
|
+
try { cpSync(srcDir, destDir, { recursive: true }); } catch {}
|
|
967
|
+
}
|
|
951
968
|
} catch (e) {
|
|
952
969
|
// Non-fatal: fall back to source path
|
|
953
970
|
}
|
|
@@ -967,32 +984,49 @@ function installClaudeCodeHookEvent(repoPath, door) {
|
|
|
967
984
|
if (!settings.hooks) settings.hooks = {};
|
|
968
985
|
if (!settings.hooks[event]) settings.hooks[event] = [];
|
|
969
986
|
|
|
970
|
-
// Match existing entries by
|
|
971
|
-
//
|
|
972
|
-
//
|
|
973
|
-
//
|
|
974
|
-
//
|
|
987
|
+
// Match existing entries by command path alone (same extension + same
|
|
988
|
+
// event). The previous finder required matcher equality too, so when
|
|
989
|
+
// an extension bumped its matcher (e.g. wip-branch-guard 1.9.78 -> 1.9.79
|
|
990
|
+
// added Read|Glob to enable onboarding), the finder missed the old entry
|
|
991
|
+
// and appended a new one, leaving an orphaned old matcher in settings
|
|
992
|
+
// and doubling hook invocations on overlapping matchers.
|
|
993
|
+
//
|
|
994
|
+
// Now: find by extension dir in the command. Update matcher + command +
|
|
995
|
+
// timeout in place. First pass removes any DUPLICATE entries for the same
|
|
996
|
+
// extension in this event slot (orphan cleanup; catches post-1.9.78
|
|
997
|
+
// duplicates on users who already installed the broken version).
|
|
975
998
|
const doorMatcher = door.matcher || undefined;
|
|
976
|
-
const
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
return cmd.includes(`/${toolName}/`) || cmd === hookCommand;
|
|
982
|
-
});
|
|
999
|
+
const toolTag = `/${toolName}/`;
|
|
1000
|
+
const ownedIdxs = [];
|
|
1001
|
+
settings.hooks[event].forEach((entry, i) => {
|
|
1002
|
+
const hooks = entry.hooks || [];
|
|
1003
|
+
if (hooks.some(h => (h.command || '').includes(toolTag))) ownedIdxs.push(i);
|
|
983
1004
|
});
|
|
1005
|
+
let removed = 0;
|
|
1006
|
+
if (ownedIdxs.length > 1) {
|
|
1007
|
+
// Keep the first, remove the rest. Walk right-to-left so earlier indices stay valid.
|
|
1008
|
+
for (let j = ownedIdxs.length - 1; j >= 1; j--) {
|
|
1009
|
+
settings.hooks[event].splice(ownedIdxs[j], 1);
|
|
1010
|
+
removed++;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
const existingIdx = ownedIdxs.length > 0 ? ownedIdxs[0] : -1;
|
|
984
1014
|
|
|
985
1015
|
if (existingIdx !== -1) {
|
|
986
|
-
const
|
|
987
|
-
|
|
1016
|
+
const existingEntry = settings.hooks[event][existingIdx];
|
|
1017
|
+
const existingCmd = existingEntry.hooks?.[0]?.command || '';
|
|
1018
|
+
const existingMatcher = existingEntry.matcher || undefined;
|
|
1019
|
+
if (existingCmd === hookCommand && existingMatcher === doorMatcher && removed === 0) {
|
|
988
1020
|
skip(`Claude Code: ${event} hook already configured`);
|
|
989
1021
|
return true;
|
|
990
1022
|
}
|
|
991
|
-
|
|
992
|
-
|
|
1023
|
+
existingEntry.matcher = doorMatcher;
|
|
1024
|
+
existingEntry.hooks[0].command = hookCommand;
|
|
1025
|
+
existingEntry.hooks[0].timeout = door.timeout || 10;
|
|
993
1026
|
try {
|
|
994
1027
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
995
|
-
|
|
1028
|
+
const note = removed > 0 ? ` (removed ${removed} orphan entr${removed === 1 ? 'y' : 'ies'})` : '';
|
|
1029
|
+
ok(`Claude Code: ${event} hook updated${note}`);
|
|
996
1030
|
return true;
|
|
997
1031
|
} catch (e) {
|
|
998
1032
|
fail(`Claude Code: failed to update settings.json. ${e.message}`);
|