@wipcomputer/wip-ldm-os 0.4.85-alpha.1 → 0.4.85-alpha.3

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/bin/ldm.js CHANGED
@@ -738,6 +738,24 @@ function deployDocs() {
738
738
  console.log(` + ${agentDocsCount} personalized doc(s) deployed to ${agentLibraryDest.replace(HOME, '~')}/`);
739
739
  }
740
740
 
741
+ // Migration-window compatibility write (added 2026-04-30).
742
+ // dev-guide-wipcomputerinc.md was migrated from ~/.ldm/shared/ to
743
+ // ~/.ldm/library/documentation/ on 2026-04-19, but agent boot files,
744
+ // ~/.claude/rules/, and other consumers still reference the old shared
745
+ // path. Without this write, the old path serves stale content and
746
+ // agents reading by the old path get pre-migration policy.
747
+ // Forward-migration (grep-update consumers, then remove this compat
748
+ // write) is tracked separately. See bugs/installer/ ticket
749
+ // 2026-04-30--cc-mini--dev-guide-split-path-migration.md.
750
+ const devGuideName = 'dev-guide-wipcomputerinc.md';
751
+ const devGuideNew = join(agentLibraryDest, devGuideName);
752
+ const devGuideOld = join(LDM_ROOT, 'shared', devGuideName);
753
+ if (existsSync(devGuideNew)) {
754
+ mkdirSync(dirname(devGuideOld), { recursive: true });
755
+ cpSync(devGuideNew, devGuideOld);
756
+ console.log(` + Compat write: ${devGuideName} also deployed to ${devGuideOld.replace(HOME, '~')} (migration window)`);
757
+ }
758
+
741
759
  return docsCount + agentDocsCount;
742
760
  }
743
761
 
package/lib/deploy.mjs CHANGED
@@ -1097,18 +1097,18 @@ function registerMCP(repoPath, door, toolName) {
1097
1097
  *
1098
1098
  * Returns true if at least one door installed successfully.
1099
1099
  */
1100
- function installClaudeCodeHook(repoPath, doorOrDoors) {
1100
+ function installClaudeCodeHook(repoPath, doorOrDoors, toolName = basename(repoPath)) {
1101
1101
  const doors = Array.isArray(doorOrDoors) ? doorOrDoors : [doorOrDoors];
1102
1102
  let anyOk = false;
1103
1103
  for (const door of doors) {
1104
- if (installClaudeCodeHookEvent(repoPath, door)) {
1104
+ if (installClaudeCodeHookEvent(repoPath, door, toolName)) {
1105
1105
  anyOk = true;
1106
1106
  }
1107
1107
  }
1108
1108
  return anyOk;
1109
1109
  }
1110
1110
 
1111
- function installClaudeCodeHookEvent(repoPath, door) {
1111
+ function installClaudeCodeHookEvent(repoPath, door, toolName = basename(repoPath)) {
1112
1112
  const settingsPath = join(HOME, '.claude', 'settings.json');
1113
1113
  let settings = readJSON(settingsPath);
1114
1114
 
@@ -1117,7 +1117,6 @@ function installClaudeCodeHookEvent(repoPath, door) {
1117
1117
  return false;
1118
1118
  }
1119
1119
 
1120
- const toolName = basename(repoPath);
1121
1120
  const extDir = join(LDM_EXTENSIONS, toolName);
1122
1121
  const installedGuard = join(extDir, 'guard.mjs');
1123
1122
 
@@ -1422,7 +1421,7 @@ export function installSingleTool(toolPath) {
1422
1421
 
1423
1422
  if (interfaces.claudeCodeHook) {
1424
1423
  if (isEnabled || isAlreadyDeployed) {
1425
- if (installClaudeCodeHook(toolPath, interfaces.claudeCodeHook)) installed++;
1424
+ if (installClaudeCodeHook(toolPath, interfaces.claudeCodeHook, toolName)) installed++;
1426
1425
  } else {
1427
1426
  skip(`Hook: ${toolName} not enabled`);
1428
1427
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ldm-os",
3
- "version": "0.4.85-alpha.1",
3
+ "version": "0.4.85-alpha.3",
4
4
  "type": "module",
5
5
  "description": "LDM OS: identity, memory, and sovereignty infrastructure for AI agents",
6
6
  "engines": {
@@ -22,6 +22,7 @@
22
22
  "validate:bin-manifest": "node scripts/validate-bin-manifest.mjs",
23
23
  "test:skill-frontmatter": "node scripts/test-skill-frontmatter.mjs",
24
24
  "test:installer-update-tracks": "node scripts/test-installer-update-tracks.mjs",
25
+ "test:installer-hook-toolname": "node scripts/test-installer-hook-toolname.mjs",
25
26
  "test:ldm-install-bin-shim": "node scripts/test-ldm-install-preserves-foreign-bin.mjs",
26
27
  "test:doctor-cron-target": "node scripts/test-doctor-cron-target.mjs",
27
28
  "test:bin-manifest": "node scripts/test-bin-manifest.mjs",
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { tmpdir } from 'node:os';
5
+
6
+ const tempHome = mkdtempSync(join(tmpdir(), 'ldm-hook-toolname-home-'));
7
+ const tempPkg = mkdtempSync(join(tmpdir(), 'ldm-npm-pack-'));
8
+
9
+ try {
10
+ process.env.HOME = tempHome;
11
+
12
+ mkdirSync(join(tempHome, '.claude'), { recursive: true });
13
+ writeFileSync(join(tempHome, '.claude', 'settings.json'), JSON.stringify({ hooks: {} }, null, 2) + '\n');
14
+ const staleExtDir = join(tempHome, '.ldm', 'extensions', 'wip-branch-guard');
15
+ mkdirSync(staleExtDir, { recursive: true });
16
+ writeFileSync(join(staleExtDir, 'guard.mjs'), 'console.log("stale guard");\n');
17
+ writeFileSync(join(staleExtDir, 'package.json'), JSON.stringify({
18
+ name: '@wipcomputer/wip-branch-guard',
19
+ version: '1.9.89',
20
+ }, null, 2) + '\n');
21
+ writeFileSync(join(tempHome, '.ldm', 'extensions', 'registry.json'), JSON.stringify({
22
+ _format: 'v2',
23
+ extensions: {
24
+ 'wip-branch-guard': {
25
+ version: '1.9.89',
26
+ ldmPath: staleExtDir,
27
+ paths: { ldm: staleExtDir },
28
+ interfaces: ['module', 'skill', 'claudeCodeHook'],
29
+ },
30
+ },
31
+ }, null, 2) + '\n');
32
+
33
+ const extractedPackageDir = join(tempPkg, 'package');
34
+ mkdirSync(extractedPackageDir, { recursive: true });
35
+ writeFileSync(join(extractedPackageDir, 'package.json'), JSON.stringify({
36
+ name: '@wipcomputer/wip-branch-guard',
37
+ version: '1.9.90',
38
+ type: 'module',
39
+ main: 'guard.mjs',
40
+ claudeCode: {
41
+ hooks: [
42
+ { event: 'PreToolUse', matcher: 'Write|Edit|Bash', command: 'node guard.mjs', timeout: 5 },
43
+ ],
44
+ },
45
+ }, null, 2) + '\n');
46
+ writeFileSync(join(extractedPackageDir, 'guard.mjs'), 'console.log("guard 1.9.90");\n');
47
+ writeFileSync(join(extractedPackageDir, 'SKILL.md'), '---\nname: wip-branch-guard\ndescription: "test skill"\n---\n');
48
+
49
+ const { installSingleTool } = await import('../lib/deploy.mjs');
50
+ const installed = installSingleTool(extractedPackageDir);
51
+ if (installed === 0) throw new Error('installer did not process the test package');
52
+
53
+ const expectedDir = join(tempHome, '.ldm', 'extensions', 'wip-branch-guard');
54
+ const wrongDir = join(tempHome, '.ldm', 'extensions', 'package');
55
+ if (!existsSync(join(expectedDir, 'guard.mjs'))) {
56
+ throw new Error('guard.mjs was not deployed under the package-derived tool name');
57
+ }
58
+ if (!existsSync(join(expectedDir, 'package.json'))) {
59
+ throw new Error('package.json was not deployed under the package-derived tool name');
60
+ }
61
+ if (existsSync(wrongDir)) {
62
+ throw new Error('hook deployment used basename(repoPath) instead of package-derived tool name');
63
+ }
64
+
65
+ const settings = JSON.parse(readFileSync(join(tempHome, '.claude', 'settings.json'), 'utf8'));
66
+ const command = settings.hooks?.PreToolUse?.[0]?.hooks?.[0]?.command || '';
67
+ if (!command.includes('/wip-branch-guard/guard.mjs')) {
68
+ throw new Error(`hook command points at the wrong extension path: ${command}`);
69
+ }
70
+
71
+ const deployedPkg = JSON.parse(readFileSync(join(expectedDir, 'package.json'), 'utf8'));
72
+ if (deployedPkg.version !== '1.9.90') {
73
+ throw new Error(`deployed package version mismatch: ${deployedPkg.version}`);
74
+ }
75
+
76
+ console.log('installer hook tool-name regression check passed');
77
+ } finally {
78
+ rmSync(tempHome, { recursive: true, force: true });
79
+ rmSync(tempPkg, { recursive: true, force: true });
80
+ }