@wipcomputer/wip-ldm-os 0.4.73-alpha.8 → 0.4.74

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.
Files changed (61) hide show
  1. package/LICENSE +52 -0
  2. package/SKILL.md +8 -1
  3. package/bin/ldm.js +587 -82
  4. package/dist/bridge/chunk-3RG5ZIWI.js +10 -0
  5. package/dist/bridge/{chunk-LF7EMFBY.js → chunk-7NH6JBIO.js} +127 -49
  6. package/dist/bridge/cli.js +2 -1
  7. package/dist/bridge/core.d.ts +13 -1
  8. package/dist/bridge/core.js +4 -1
  9. package/dist/bridge/mcp-server.js +52 -7
  10. package/dist/bridge/openclaw.d.ts +5 -0
  11. package/dist/bridge/openclaw.js +11 -0
  12. package/docs/bridge/TECHNICAL.md +86 -0
  13. package/docs/doc-pipeline/README.md +74 -0
  14. package/docs/doc-pipeline/TECHNICAL.md +79 -0
  15. package/lib/deploy.mjs +175 -13
  16. package/lib/detect.mjs +20 -6
  17. package/package.json +2 -2
  18. package/shared/docs/README.md.tmpl +2 -2
  19. package/shared/docs/how-releases-work.md.tmpl +3 -1
  20. package/shared/docs/how-worktrees-work.md.tmpl +12 -7
  21. package/shared/rules/git-conventions.md +3 -3
  22. package/shared/rules/release-pipeline.md +1 -1
  23. package/shared/rules/security.md +1 -1
  24. package/shared/rules/workspace-boundaries.md +1 -1
  25. package/shared/rules/writing-style.md +1 -1
  26. package/shared/templates/claude-md-level1.md +7 -3
  27. package/src/bridge/core.ts +160 -56
  28. package/src/bridge/mcp-server.ts +93 -8
  29. package/src/bridge/openclaw.ts +14 -0
  30. package/src/hooks/inbox-check-hook.mjs +232 -0
  31. package/src/hooks/inbox-rewake-hook.mjs +388 -0
  32. package/src/hosted-mcp/.env.example +3 -0
  33. package/src/hosted-mcp/demo/agent.html +300 -0
  34. package/src/hosted-mcp/demo/agent.txt +84 -0
  35. package/src/hosted-mcp/demo/fallback.jpg +0 -0
  36. package/src/hosted-mcp/demo/footer.js +74 -0
  37. package/src/hosted-mcp/demo/index.html +1303 -0
  38. package/src/hosted-mcp/demo/login.html +548 -0
  39. package/src/hosted-mcp/demo/privacy.html +223 -0
  40. package/src/hosted-mcp/demo/sprites.jpg +0 -0
  41. package/src/hosted-mcp/demo/sprites.png +0 -0
  42. package/src/hosted-mcp/demo/tos.html +198 -0
  43. package/src/hosted-mcp/deploy.sh +70 -0
  44. package/src/hosted-mcp/ecosystem.config.cjs +14 -0
  45. package/src/hosted-mcp/inbox.mjs +64 -0
  46. package/src/hosted-mcp/legal/internet-services/terms/site.html +205 -0
  47. package/src/hosted-mcp/legal/privacy/en-ww/index.html +230 -0
  48. package/src/hosted-mcp/nginx/mcp-oauth.conf +98 -0
  49. package/src/hosted-mcp/nginx/mcp-server.conf +17 -0
  50. package/src/hosted-mcp/nginx/wip.computer.conf +45 -0
  51. package/src/hosted-mcp/package-lock.json +2092 -0
  52. package/src/hosted-mcp/package.json +23 -0
  53. package/src/hosted-mcp/prisma/migrations/20260406233014_init/migration.sql +68 -0
  54. package/src/hosted-mcp/prisma/migrations/migration_lock.toml +3 -0
  55. package/src/hosted-mcp/prisma/schema.prisma +57 -0
  56. package/src/hosted-mcp/prisma.config.ts +14 -0
  57. package/src/hosted-mcp/server.mjs +2093 -0
  58. package/src/hosted-mcp/shared/kaleidoscope.css +139 -0
  59. package/src/hosted-mcp/shared/kaleidoscope.js +192 -0
  60. package/src/hosted-mcp/tools.mjs +73 -0
  61. package/templates/hooks/pre-commit +5 -0
@@ -0,0 +1,79 @@
1
+ # Documentation Pipeline: Technical Details
2
+
3
+ ## File Paths
4
+
5
+ ### Repo doc templates (in the LDM OS repo)
6
+
7
+ ```
8
+ shared/docs/*.md.tmpl Templates for home docs
9
+ shared/rules/*.md Source for agent rules
10
+ shared/boot/ Boot sequence config
11
+ ```
12
+
13
+ Templates use placeholders from `~/.ldm/config.json` (workspace path, agent names, org name, etc.).
14
+
15
+ ### Installed locations
16
+
17
+ ```
18
+ ~/.ldm/config.json Org config (agents, paths, co-authors)
19
+ ~/.ldm/shared/rules/*.md Agent rules (source for ~/.claude/rules/)
20
+ ~/.ldm/shared/dev-guide-*.md Org dev guide
21
+ ~/.ldm/shared/boot/ Boot sequence config
22
+ ~/.ldm/shared/prompts/ Cron prompts
23
+ ~/.ldm/templates/ CLAUDE.md templates, install prompt, etc.
24
+ ~/wipcomputerinc/library/documentation/ Personalized human docs (from templates)
25
+ ~/.claude/rules/ Deployed rules (copied from ~/.ldm/shared/rules/)
26
+ ~/.claude/CLAUDE.md Level 1 global (generated from template + config)
27
+ ```
28
+
29
+ ### What ldm install deploys
30
+
31
+ | Source | Destination | When |
32
+ |--------|------------|------|
33
+ | `shared/rules/*.md` | `~/.ldm/shared/rules/` then `~/.claude/rules/` | Every install |
34
+ | `shared/docs/*.md.tmpl` | `~/wipcomputerinc/library/documentation/` | Every install |
35
+ | `shared/boot/` | `~/.ldm/shared/boot/` | Every install |
36
+ | `shared/prompts/` | `~/.ldm/shared/prompts/` | Every install |
37
+ | `shared/templates/` | `~/.ldm/templates/` | Every install |
38
+
39
+ **Known bug (April 2026):** The installer currently deploys shared rules on `ldm init` (first install) but not on every `ldm install`. This means rule updates in new versions don't propagate until the user re-initializes. This needs to be fixed so rules deploy on every install.
40
+
41
+ **Known bug (April 2026):** The installer previously deployed home docs to `settings/docs/`. This path was renamed to `library/documentation/` on March 28, 2026. The installer must deploy to the correct path.
42
+
43
+ ## How templates work
44
+
45
+ Home doc templates at `shared/docs/*.md.tmpl` contain placeholders:
46
+
47
+ ```
48
+ Workspace: {{workspace}}
49
+ Agents: {{agents}}
50
+ ```
51
+
52
+ The installer reads `~/.ldm/config.json`, substitutes the placeholders, and writes the personalized docs to `~/wipcomputerinc/library/documentation/`.
53
+
54
+ ## CLAUDE.md cascade
55
+
56
+ Three levels. Claude Code reads all of them, walking up from CWD:
57
+
58
+ ```
59
+ Level 1: ~/.claude/CLAUDE.md Global. ~30 lines. Universal rules.
60
+ Level 2: ~/wipcomputerinc/CLAUDE.md Workspace. ~150 lines. Org context.
61
+ Level 3: <repo>/CLAUDE.md Per-repo. ~50-86 lines. Repo-specific.
62
+ ```
63
+
64
+ After context compaction, CWD may shift to a repo. Without Level 3, all context is lost. Every repo must have a CLAUDE.md.
65
+
66
+ ## Config paths (correct as of April 2026)
67
+
68
+ References in CLAUDE.md and rules must use absolute paths:
69
+
70
+ | Reference | Correct path |
71
+ |-----------|-------------|
72
+ | Org config | `~/.ldm/config.json` |
73
+ | Dev guide | `~/.ldm/shared/dev-guide-wipcomputerinc.md` |
74
+ | Human docs | `~/wipcomputerinc/library/documentation/` |
75
+ | Agent rules | `~/.ldm/shared/rules/` (source) or `~/.claude/rules/` (deployed) |
76
+ | Workspace CLAUDE.md | `~/wipcomputerinc/CLAUDE.md` |
77
+ | Global CLAUDE.md | `~/.claude/CLAUDE.md` |
78
+
79
+ NOT `settings/config.json`. NOT `settings/docs/`. Those paths are from before the March 28 rename.
package/lib/deploy.mjs CHANGED
@@ -394,14 +394,38 @@ function runBuildIfNeeded(repoPath) {
394
394
 
395
395
  function compareSemver(a, b) {
396
396
  if (!a || !b) return 0;
397
- const pa = a.split('.').map(Number);
398
- const pb = b.split('.').map(Number);
397
+ if (a === b) return 0;
398
+ const [aBase, aPre] = a.split('-', 2);
399
+ const [bBase, bPre] = b.split('-', 2);
400
+ const pa = aBase.split('.').map(Number);
401
+ const pb = bBase.split('.').map(Number);
399
402
  for (let i = 0; i < 3; i++) {
400
403
  const na = pa[i] || 0;
401
404
  const nb = pb[i] || 0;
402
405
  if (na > nb) return 1;
403
406
  if (na < nb) return -1;
404
407
  }
408
+ // Base versions equal. Stable > prerelease.
409
+ if (!aPre && bPre) return 1; // a is stable, b is prerelease -> a is newer
410
+ if (aPre && !bPre) return -1; // a is prerelease, b is stable -> b is newer
411
+ // Both have prereleases. Compare segments.
412
+ if (aPre && bPre) {
413
+ const aSegs = aPre.split('.');
414
+ const bSegs = bPre.split('.');
415
+ for (let i = 0; i < Math.max(aSegs.length, bSegs.length); i++) {
416
+ const as = aSegs[i] || '';
417
+ const bs = bSegs[i] || '';
418
+ const an = Number(as);
419
+ const bn = Number(bs);
420
+ if (!isNaN(an) && !isNaN(bn)) {
421
+ if (an > bn) return 1;
422
+ if (an < bn) return -1;
423
+ } else {
424
+ if (as > bs) return 1;
425
+ if (as < bs) return -1;
426
+ }
427
+ }
428
+ }
405
429
  return 0;
406
430
  }
407
431
 
@@ -508,6 +532,81 @@ function safeDeployDir(repoPath, destDir, name) {
508
532
  }
509
533
  }
510
534
 
535
+ /**
536
+ * Update tools.allow in openclaw.json to include a newly deployed plugin.
537
+ * OpenClaw 2026.4.8+ enforces tools.allow as an exclusive allowlist.
538
+ * Without this, newly installed plugins are blocked from running.
539
+ *
540
+ * This function MUST remain at module top level. Nesting it inside another
541
+ * function puts it out of scope for its call sites in the install handlers
542
+ * and produces a ReferenceError at runtime. See:
543
+ * ai/product/bugs/installer/2026-04-11--cc-mini--update-tools-allow-reference-error.md
544
+ */
545
+ function updateToolsAllow(pluginName) {
546
+ const ocConfigPath = join(OC_ROOT, 'openclaw.json');
547
+ if (!existsSync(ocConfigPath)) return;
548
+ try {
549
+ const raw = readFileSync(ocConfigPath, 'utf8');
550
+ const config = JSON.parse(raw);
551
+ if (!config.tools?.allow || !Array.isArray(config.tools.allow)) return;
552
+ if (config.tools.allow.includes(pluginName)) return;
553
+ config.tools.allow.push(pluginName);
554
+ writeFileSync(ocConfigPath, JSON.stringify(config, null, 2) + '\n');
555
+ log(`Added "${pluginName}" to openclaw.json tools.allow`);
556
+ } catch (e) {
557
+ log(`Warning: failed to update tools.allow for ${pluginName}: ${e.message}`);
558
+ }
559
+ }
560
+
561
+ /**
562
+ * Reconcile tools.allow against plugins.entries in ~/.openclaw/openclaw.json.
563
+ *
564
+ * In OpenClaw 2026.4.8+, any plugin registered in plugins.entries but missing
565
+ * from tools.allow is silently blocked at runtime. Each blocked tool call
566
+ * spawns a root-key approval prompt to the user, flooding iMessage with
567
+ * approve-ids. This was observed on 2026-04-11 for model-provider plugins
568
+ * (anthropic, openai, xai) and imessage, which were enabled in plugins.entries
569
+ * but never added to tools.allow, because the per-plugin updateToolsAllow path
570
+ * only runs during new plugin deploys and the alpha.27/28 ReferenceError had
571
+ * silently dropped those entries anyway.
572
+ *
573
+ * This function is the self-healing step: at install time, walk plugins.entries,
574
+ * find any enabled plugin whose name is not already in tools.allow, and add it.
575
+ * Idempotent. Disabled plugins are skipped. Runs at both ends of installFromPath
576
+ * so a single `ldm install` repairs existing broken state without requiring a
577
+ * new plugin deploy.
578
+ *
579
+ * Background:
580
+ * ai/product/bugs/code-fka-devopstoolkit/2026-04-11--cc-mini--update-tools-allow-reference-error.md
581
+ *
582
+ * This function MUST remain at module top level, same as updateToolsAllow.
583
+ */
584
+ function reconcileToolsAllow() {
585
+ const ocConfigPath = join(OC_ROOT, 'openclaw.json');
586
+ if (!existsSync(ocConfigPath)) return;
587
+ try {
588
+ const raw = readFileSync(ocConfigPath, 'utf8');
589
+ const config = JSON.parse(raw);
590
+ if (!config.plugins?.entries || typeof config.plugins.entries !== 'object') return;
591
+ if (!config.tools?.allow || !Array.isArray(config.tools.allow)) return;
592
+
593
+ const enabledPlugins = Object.entries(config.plugins.entries)
594
+ .filter(([, entry]) => entry && entry.enabled !== false)
595
+ .map(([name]) => name);
596
+
597
+ const allow = config.tools.allow;
598
+ const missing = enabledPlugins.filter(name => !allow.includes(name));
599
+
600
+ if (missing.length === 0) return;
601
+
602
+ for (const name of missing) allow.push(name);
603
+ writeFileSync(ocConfigPath, JSON.stringify(config, null, 2) + '\n');
604
+ log(`Reconciled openclaw.json tools.allow: added ${missing.join(', ')}`);
605
+ } catch (e) {
606
+ log(`Warning: failed to reconcile tools.allow: ${e.message}`);
607
+ }
608
+ }
609
+
511
610
  // ── OpenClaw plugin naming (fix #8) ──
512
611
 
513
612
  function resolveOcPluginName(repoPath, toolName) {
@@ -768,7 +867,6 @@ function registerMCP(repoPath, door, toolName) {
768
867
  const envFlag = existsSync(OC_ROOT) ? ` -e OPENCLAW_HOME="${OC_ROOT}"` : '';
769
868
  execSync(`claude mcp add --scope user ${name}${envFlag} -- node "${mcpPath}"`, { stdio: 'pipe' });
770
869
  ok(`MCP: registered ${name} at user scope`);
771
- return true;
772
870
  } catch (e) {
773
871
  // Fallback: write to ~/.claude.json directly
774
872
  try {
@@ -780,15 +878,53 @@ function registerMCP(repoPath, door, toolName) {
780
878
  };
781
879
  writeJSON(ccUserPath, mcpConfig);
782
880
  ok(`MCP: registered ${name} in ~/.claude.json (fallback)`);
783
- return true;
784
881
  } catch (e2) {
785
882
  fail(`MCP: registration failed. ${e.message}`);
786
883
  return false;
787
884
  }
788
885
  }
886
+
887
+ // Also register with OpenClaw so the MCP tools are available to all
888
+ // OpenClaw agents (e.g. Lēsa) without exec-approval gates. CC
889
+ // registration alone only gives tools to Claude Code sessions, not
890
+ // to OpenClaw's agent pipeline. Discovered 2026-04-11 when Lēsa
891
+ // lost xAI image gen tools after switching from Grok to Claude CLI.
892
+ try {
893
+ const ocMcpConfig = JSON.stringify({ command: 'node', args: [mcpPath] });
894
+ execSync(`openclaw mcp set ${name} '${ocMcpConfig}'`, { stdio: 'pipe' });
895
+ ok(`MCP: registered ${name} with OpenClaw`);
896
+ } catch {
897
+ // Non-fatal: OpenClaw may not be installed on all machines
898
+ }
899
+
900
+ // Add to OpenClaw tools.allow so the MCP tools are pre-authorized
901
+ updateToolsAllow(name);
902
+
903
+ return true;
904
+ }
905
+
906
+ /**
907
+ * Install Claude Code hook(s) for an extension.
908
+ *
909
+ * Accepts either a single door object (legacy) or an array of door objects
910
+ * (new in 2026-04-05 for wip-branch-guard 1.9.73 which registers on both
911
+ * PreToolUse and SessionStart). Normalizes to an array and installs each
912
+ * door independently.
913
+ *
914
+ * Returns true if at least one door installed successfully.
915
+ */
916
+ function installClaudeCodeHook(repoPath, doorOrDoors) {
917
+ const doors = Array.isArray(doorOrDoors) ? doorOrDoors : [doorOrDoors];
918
+ let anyOk = false;
919
+ for (const door of doors) {
920
+ if (installClaudeCodeHookEvent(repoPath, door)) {
921
+ anyOk = true;
922
+ }
923
+ }
924
+ return anyOk;
789
925
  }
790
926
 
791
- function installClaudeCodeHook(repoPath, door) {
927
+ function installClaudeCodeHookEvent(repoPath, door) {
792
928
  const settingsPath = join(HOME, '.claude', 'settings.json');
793
929
  let settings = readJSON(settingsPath);
794
930
 
@@ -802,6 +938,8 @@ function installClaudeCodeHook(repoPath, door) {
802
938
  const installedGuard = join(extDir, 'guard.mjs');
803
939
 
804
940
  // Deploy guard.mjs to ~/.ldm/extensions/{toolName}/ (#85: always update, not just when missing)
941
+ // Idempotent across multi-door invocations: two doors on the same repo
942
+ // will both trigger this copy, which is a filesystem no-op after the first.
805
943
  const srcGuard = join(repoPath, 'guard.mjs');
806
944
  if (existsSync(srcGuard)) {
807
945
  try {
@@ -819,21 +957,30 @@ function installClaudeCodeHook(repoPath, door) {
819
957
  ? `node ${installedGuard}`
820
958
  : (door.command || `node "${srcGuard}"`);
821
959
 
960
+ const event = door.event || 'PreToolUse';
961
+
822
962
  if (DRY_RUN) {
823
- ok(`Claude Code: would add ${door.event || 'PreToolUse'} hook (dry run)`);
963
+ ok(`Claude Code: would add ${event} hook (dry run)`);
824
964
  return true;
825
965
  }
826
966
 
827
967
  if (!settings.hooks) settings.hooks = {};
828
- const event = door.event || 'PreToolUse';
829
968
  if (!settings.hooks[event]) settings.hooks[event] = [];
830
969
 
831
- const existingIdx = settings.hooks[event].findIndex(entry =>
832
- entry.hooks?.some(h => {
970
+ // Match existing entries by the guard command path + the matcher, so that
971
+ // a single extension registering on multiple events (each with its own
972
+ // matcher) creates one entry per event rather than all entries colliding
973
+ // on the same hook slot. Before this change the existing-entry check was
974
+ // per-extension, not per-extension-per-event.
975
+ const doorMatcher = door.matcher || undefined;
976
+ const existingIdx = settings.hooks[event].findIndex(entry => {
977
+ const sameMatcher = (entry.matcher || undefined) === doorMatcher;
978
+ if (!sameMatcher) return false;
979
+ return entry.hooks?.some(h => {
833
980
  const cmd = h.command || '';
834
981
  return cmd.includes(`/${toolName}/`) || cmd === hookCommand;
835
- })
836
- );
982
+ });
983
+ });
837
984
 
838
985
  if (existingIdx !== -1) {
839
986
  const existingCmd = settings.hooks[event][existingIdx].hooks?.[0]?.command || '';
@@ -854,7 +1001,7 @@ function installClaudeCodeHook(repoPath, door) {
854
1001
  }
855
1002
 
856
1003
  settings.hooks[event].push({
857
- matcher: door.matcher || undefined,
1004
+ matcher: doorMatcher,
858
1005
  hooks: [{
859
1006
  type: 'command',
860
1007
  command: hookCommand,
@@ -1016,6 +1163,8 @@ export function installSingleTool(toolPath) {
1016
1163
  installed++;
1017
1164
  registryInfo.ldmPath = join(LDM_EXTENSIONS, toolName);
1018
1165
  registryInfo.ocPath = join(OC_EXTENSIONS, toolName);
1166
+ // Update tools.allow in openclaw.json so OC 2026.4.8+ doesn't block the plugin
1167
+ updateToolsAllow(toolName);
1019
1168
  }
1020
1169
  } else if (interfaces.mcp) {
1021
1170
  const extName = basename(toolPath);
@@ -1115,10 +1264,19 @@ export function installToolbox(repoPath) {
1115
1264
  // ── Full install pipeline ──
1116
1265
 
1117
1266
  export async function installFromPath(repoPath) {
1267
+ // Heal tools.allow before any install runs, so the current session picks up
1268
+ // any drift left by earlier broken installs (alpha.27/28 ReferenceError).
1269
+ // Idempotent: no-op if plugins.entries and tools.allow are already in sync.
1270
+ reconcileToolsAllow();
1271
+
1118
1272
  const subTools = detectToolbox(repoPath);
1119
1273
 
1120
1274
  if (subTools.length > 0) {
1121
- return installToolbox(repoPath);
1275
+ const result = installToolbox(repoPath);
1276
+ // Heal again after toolbox install in case any plugin was newly registered
1277
+ // in plugins.entries but never added to tools.allow by its deploy path.
1278
+ reconcileToolsAllow();
1279
+ return result;
1122
1280
  }
1123
1281
 
1124
1282
  const installed = installSingleTool(repoPath);
@@ -1135,6 +1293,10 @@ export async function installFromPath(repoPath) {
1135
1293
  console.log('');
1136
1294
  }
1137
1295
 
1296
+ // Final reconcile pass after single-tool install, for the same reason as
1297
+ // the toolbox branch above.
1298
+ reconcileToolsAllow();
1299
+
1138
1300
  return { tools: 1, interfaces: installed };
1139
1301
  }
1140
1302
 
package/lib/detect.mjs CHANGED
@@ -53,16 +53,27 @@ export function detectInterfaces(repoPath) {
53
53
  interfaces.skill = { path: join(repoPath, 'SKILL.md') };
54
54
  }
55
55
 
56
- // 6. Claude Code Hook: guard.mjs or claudeCode.hook in package.json
57
- if (pkg?.claudeCode?.hook) {
58
- interfaces.claudeCodeHook = pkg.claudeCode.hook;
56
+ // 6. Claude Code Hook: guard.mjs or claudeCode.hook(s) in package.json
57
+ //
58
+ // Supports three shapes:
59
+ // - Legacy singular: pkg.claudeCode.hook = { event, matcher, ... }
60
+ // - New plural array: pkg.claudeCode.hooks = [{ event, matcher, ... }, ...]
61
+ // (one extension can register on multiple events, e.g. both PreToolUse
62
+ // and SessionStart. Added 2026-04-05 for wip-branch-guard 1.9.73.)
63
+ // - Implicit: a bare guard.mjs file with no package.json declaration.
64
+ //
65
+ // Normalized to an array internally so deploy.mjs has one code path.
66
+ if (Array.isArray(pkg?.claudeCode?.hooks)) {
67
+ interfaces.claudeCodeHook = pkg.claudeCode.hooks;
68
+ } else if (pkg?.claudeCode?.hook) {
69
+ interfaces.claudeCodeHook = [pkg.claudeCode.hook];
59
70
  } else if (existsSync(join(repoPath, 'guard.mjs'))) {
60
- interfaces.claudeCodeHook = {
71
+ interfaces.claudeCodeHook = [{
61
72
  event: 'PreToolUse',
62
73
  matcher: 'Edit|Write',
63
74
  command: `node "${join(repoPath, 'guard.mjs')}"`,
64
75
  timeout: 5,
65
- };
76
+ }];
66
77
  }
67
78
 
68
79
  // 7. Claude Code Plugin: .claude-plugin/plugin.json
@@ -93,7 +104,10 @@ export function describeInterfaces(interfaces) {
93
104
  if (interfaces.mcp) lines.push(`MCP Server: ${interfaces.mcp.file}`);
94
105
  if (interfaces.openclaw) lines.push(`OpenClaw Plugin: ${interfaces.openclaw.config?.name || 'detected'}`);
95
106
  if (interfaces.skill) lines.push(`Skill: SKILL.md`);
96
- if (interfaces.claudeCodeHook) lines.push(`Claude Code Hook: ${interfaces.claudeCodeHook.event || 'PreToolUse'}`);
107
+ if (interfaces.claudeCodeHook) {
108
+ const events = interfaces.claudeCodeHook.map(h => h.event || 'PreToolUse');
109
+ lines.push(`Claude Code Hook: ${events.join(', ')}`);
110
+ }
97
111
  if (interfaces.claudeCodePlugin) lines.push(`Claude Code Plugin: ${interfaces.claudeCodePlugin.manifest?.name || 'detected'}`);
98
112
 
99
113
  return `${names.length} interface(s): ${names.join(', ')}\n${lines.map(l => ` ${l}`).join('\n')}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ldm-os",
3
- "version": "0.4.73-alpha.8",
3
+ "version": "0.4.74",
4
4
  "type": "module",
5
5
  "description": "LDM OS: identity, memory, and sovereignty infrastructure for AI agents",
6
6
  "engines": {
@@ -16,7 +16,7 @@
16
16
  "lesa": "dist/bridge/cli.js"
17
17
  },
18
18
  "scripts": {
19
- "build:bridge": "cd src/bridge && npm install && npx tsup core.ts mcp-server.ts cli.ts --format esm --dts --clean --outDir ../../dist/bridge",
19
+ "build:bridge": "cd src/bridge && npm install && npx tsup core.ts mcp-server.ts cli.ts openclaw.ts --format esm --dts --clean --outDir ../../dist/bridge",
20
20
  "build": "npm run build:bridge",
21
21
  "prepublishOnly": "npm run build:bridge",
22
22
  "fmt": "npx prettier --write 'src/**/*.{ts,mjs}' 'lib/**/*.mjs' 'bin/**/*.js'",
@@ -6,7 +6,7 @@ Updates come from two sources:
6
6
  - **config.json** ... when you change your org settings, the "Your System" sections regenerate
7
7
  - **System hooks** ... when tools are installed, updated, or reconfigured, the relevant docs update automatically
8
8
 
9
- If something is wrong, update `settings/config.json` or run `ldm install`. The docs will follow.
9
+ If something is wrong, update `~/.ldm/config.json` or run `ldm install`. The docs will follow.
10
10
 
11
11
  ## What's Here
12
12
 
@@ -32,4 +32,4 @@ Each doc has two sections:
32
32
  1. **Universal** ... how the feature works for everyone (top of file)
33
33
  2. **Your System** ... your specific configuration (bottom, after the `---` separator)
34
34
 
35
- Universal content comes from the LDM OS repo. "Your System" content is generated from your `settings/config.json`.
35
+ Universal content comes from the LDM OS repo. "Your System" content is generated from your `~/.ldm/config.json`.
@@ -23,8 +23,10 @@ cd .worktrees/repo--my-prefix--feature/
23
23
  git push -u origin my-prefix/feature
24
24
  gh pr create && gh pr merge --merge
25
25
 
26
- # 3. Alpha release
26
+ # 3. ALWAYS pull to main after merge (not optional)
27
27
  cd /path/to/repo && git checkout main && git pull
28
+
29
+ # 4. Alpha release
28
30
  wip-release alpha --notes="what changed"
29
31
 
30
32
  # 4. Install and test
@@ -10,8 +10,8 @@ A git worktree is a second checkout of the same repo. Same history, same remote,
10
10
 
11
11
  ```
12
12
  my-repo/ <- main branch (read-only)
13
- _worktrees/my-repo--fix-bug/ <- your worktree (editable)
14
- _worktrees/my-repo--new-feature/ <- someone else's worktree
13
+ .worktrees/my-repo--fix-bug/ <- your worktree (editable)
14
+ .worktrees/my-repo--new-feature/ <- someone else's worktree
15
15
  ```
16
16
 
17
17
  All share the same `.git` database. Commits in any worktree are visible to all. But each has its own branch and files on disk.
@@ -23,22 +23,27 @@ cd my-repo
23
23
  ldm worktree add my-prefix/fix-bug
24
24
  ```
25
25
 
26
- This creates `_worktrees/my-repo--my-prefix--fix-bug/`.
26
+ This creates `.worktrees/my-repo--my-prefix--fix-bug/`.
27
27
 
28
28
  ## How to Work
29
29
 
30
30
  Edit files in the worktree directory. Commit, push, PR, merge as normal:
31
31
 
32
32
  ```bash
33
- cd _worktrees/my-repo--my-prefix--fix-bug/
33
+ cd .worktrees/my-repo--my-prefix--fix-bug/
34
34
  # edit, then:
35
35
  git add <files>
36
36
  git commit -m "description"
37
37
  git push -u origin my-prefix/fix-bug
38
38
  gh pr create
39
39
  gh pr merge --merge --delete-branch
40
+
41
+ # CRITICAL: pull to main immediately after merge
42
+ cd /path/to/repo && git checkout main && git pull
40
43
  ```
41
44
 
45
+ **Always pull to main after merging a PR.** If you don't, the main working tree is stale and files won't show up. This is not optional. Every merge, every time.
46
+
42
47
  ## How to Clean Up
43
48
 
44
49
  ```bash
@@ -65,13 +70,13 @@ Switching branches changes every file in the directory. If another process (an a
65
70
 
66
71
  ## Your System
67
72
 
68
- **Worktree location:** `~/wipcomputerinc/repos/_worktrees/`
73
+ **Worktree location:** `~/wipcomputerinc/repos/.worktrees/`
69
74
 
70
75
  **Branch prefixes:**
71
76
  - `cc-mini/` ... Claude Code on Mac mini
72
77
  - `cc-air/` ... Claude Code on MacBook Air
73
78
  - `lesa-mini/` ... Lesa on Mac mini
74
79
 
75
- **Guard:** The branch guard warns if you create a worktree outside `_worktrees/`. Suggests `ldm worktree add` instead.
80
+ **Guard:** The branch guard warns if you create a worktree outside `.worktrees/`. Suggests `ldm worktree add` instead.
76
81
 
77
- **Auto-cleanup:** `wip-release` prunes merged worktrees from `_worktrees/` after every release.
82
+ **Auto-cleanup:** `wip-release` prunes merged worktrees from `.worktrees/` after every release.
@@ -14,11 +14,11 @@ Always use a branch and PR.
14
14
 
15
15
  ## Co-authors on every commit
16
16
 
17
- List all contributors. Read co-author lines from `settings/config.json` in your workspace.
17
+ List all contributors. Read co-author lines from `~/.ldm/config.json` coAuthors field.
18
18
 
19
19
  ## Branch prefixes
20
20
 
21
- Each agent uses a prefix from `settings/config.json` agents section. Prevents collisions.
21
+ Each agent uses a prefix from `~/.ldm/config.json` agents section. Prevents collisions.
22
22
 
23
23
  ## Worktrees
24
24
 
@@ -30,4 +30,4 @@ For private/public repo pairs, all issues go on the public repo.
30
30
 
31
31
  ## On-demand reference
32
32
 
33
- Before doing repo work, read `~/wipcomputerinc/settings/docs/how-worktrees-work.md` for the full worktree workflow with commands.
33
+ Before doing repo work, read `~/wipcomputerinc/library/documentation/how-worktrees-work.md` for the full worktree workflow with commands.
@@ -39,4 +39,4 @@ Installed tools are for execution. Repo clones are for development. Use the inst
39
39
 
40
40
  ## On-demand reference
41
41
 
42
- Before releasing, read `~/wipcomputerinc/settings/docs/how-releases-work.md` for the full pipeline with commands.
42
+ Before releasing, read `~/wipcomputerinc/library/documentation/how-releases-work.md` for the full pipeline with commands.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Secret management
4
4
 
5
- Use your org's secret management tool (configured in settings/config.json). Never hardcode API keys, tokens, or credentials.
5
+ Use your org's secret management tool (configured in `~/.ldm/config.json`). Never hardcode API keys, tokens, or credentials.
6
6
 
7
7
  ## Security audit before installing anything
8
8
 
@@ -22,4 +22,4 @@ Installed tools are for execution. Repo clones are for development. Use the inst
22
22
 
23
23
  ## On-demand reference
24
24
 
25
- For the full directory map, read `~/wipcomputerinc/settings/docs/system-directories.md`.
25
+ For the full directory map, read `~/wipcomputerinc/library/documentation/system-directories.md`.
@@ -1,5 +1,5 @@
1
1
  # Writing Style
2
2
 
3
- Read writing conventions from your org's `settings/config.json` writingStyle section.
3
+ Read writing conventions from `~/.ldm/config.json` writingStyle section.
4
4
 
5
5
  **Full paths in documentation.** Never truncate paths. Always show the complete path so there's no ambiguity.
@@ -5,9 +5,13 @@
5
5
  Never use em dashes. Use periods, colons, semicolons, or ellipsis (...) instead.
6
6
  Timezone: PST (Pacific), 24-hour clock. Parker is in Los Angeles.
7
7
 
8
+ ## Don't Hedge
9
+
10
+ Never ask "should I stop?", "is this too much?", "what should we do now?", or "do you want me to continue?". If you have work to do, do it. If you're stuck, say what you're stuck on specifically. Don't express existential doubt about the task. Don't ask permission to keep working. Don't narrate your own uncertainty. Just work.
11
+
8
12
  ## Co-Authors on Every Commit
9
13
 
10
- Read co-author lines from `~/wipcomputerinc/settings/config.json` coAuthors field. All contributors listed on every commit. No exceptions.
14
+ Read co-author lines from `~/.ldm/config.json` coAuthors field. All contributors listed on every commit. No exceptions.
11
15
 
12
16
  ## 1Password CLI: Always Use Service Account Token
13
17
 
@@ -30,8 +34,8 @@ Before reaching for any external service or workaround: search memory first. Use
30
34
 
31
35
  ## Dev Conventions
32
36
 
33
- For git workflow, releases, worktrees, and repo conventions: read `~/wipcomputerinc/settings/docs/` on demand when doing repo work. Key docs:
37
+ For git workflow, releases, worktrees, and repo conventions: read `~/wipcomputerinc/library/documentation/` on demand when doing repo work. Key docs:
34
38
  - `how-worktrees-work.md` ... git worktrees, the convention, commands
35
39
  - `how-releases-work.md` ... the full release pipeline
36
40
  - `system-directories.md` ... what lives where
37
- - Also read `~/wipcomputerinc/settings/templates/dev-guide-private.md` for org-specific conventions
41
+ - Also read `~/.ldm/shared/dev-guide-wipcomputerinc.md` for org-specific conventions