clawvault 3.0.0 → 3.1.0

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 (60) hide show
  1. package/README.md +156 -105
  2. package/bin/clawvault.js +0 -2
  3. package/bin/register-core-commands.js +20 -2
  4. package/dist/{chunk-3D6BCTP6.js → chunk-33UGEQRT.js} +70 -145
  5. package/dist/{chunk-ZVVFWOLW.js → chunk-3WRJEKN4.js} +1 -1
  6. package/dist/{chunk-DEFFDRVP.js → chunk-3ZIH425O.js} +3 -70
  7. package/dist/{chunk-K234IDRJ.js → chunk-D2H45LON.js} +1 -0
  8. package/dist/{chunk-YKTA5JOJ.js → chunk-H62BP7RI.js} +3 -3
  9. package/dist/{chunk-WGRQ6HDV.js → chunk-LI4O6NVK.js} +1 -1
  10. package/dist/{chunk-7R7O6STJ.js → chunk-OCGVIN3L.js} +1 -1
  11. package/dist/{chunk-GAJV4IGR.js → chunk-YCUNCH2I.js} +3 -7
  12. package/dist/cli/index.cjs +10 -1459
  13. package/dist/cli/index.js +5 -8
  14. package/dist/commands/compat.cjs +70 -145
  15. package/dist/commands/compat.js +1 -1
  16. package/dist/commands/context.cjs +1 -0
  17. package/dist/commands/context.js +3 -3
  18. package/dist/commands/doctor.cjs +68 -144
  19. package/dist/commands/doctor.js +4 -4
  20. package/dist/commands/embed.js +2 -2
  21. package/dist/commands/setup.cjs +2 -69
  22. package/dist/commands/setup.d.cts +0 -1
  23. package/dist/commands/setup.d.ts +0 -1
  24. package/dist/commands/setup.js +2 -2
  25. package/dist/commands/sleep.cjs +1 -0
  26. package/dist/commands/sleep.js +2 -2
  27. package/dist/commands/status.cjs +1 -0
  28. package/dist/commands/status.js +2 -2
  29. package/dist/commands/wake.cjs +1 -0
  30. package/dist/commands/wake.js +2 -2
  31. package/dist/index.cjs +447 -2600
  32. package/dist/index.d.cts +0 -4
  33. package/dist/index.d.ts +0 -4
  34. package/dist/index.js +8 -69
  35. package/dist/plugin/index.cjs +3 -3
  36. package/dist/plugin/index.js +10 -10
  37. package/package.json +11 -17
  38. package/bin/register-tailscale-commands.js +0 -106
  39. package/dist/chunk-IVRIKYFE.js +0 -520
  40. package/dist/chunk-THRJVD4L.js +0 -373
  41. package/dist/chunk-TIGW564L.js +0 -628
  42. package/dist/commands/tailscale.cjs +0 -1532
  43. package/dist/commands/tailscale.d.cts +0 -52
  44. package/dist/commands/tailscale.d.ts +0 -52
  45. package/dist/commands/tailscale.js +0 -26
  46. package/dist/lib/canvas-layout.cjs +0 -136
  47. package/dist/lib/canvas-layout.d.cts +0 -31
  48. package/dist/lib/canvas-layout.d.ts +0 -31
  49. package/dist/lib/canvas-layout.js +0 -92
  50. package/dist/lib/tailscale.cjs +0 -1183
  51. package/dist/lib/tailscale.d.cts +0 -225
  52. package/dist/lib/tailscale.d.ts +0 -225
  53. package/dist/lib/tailscale.js +0 -50
  54. package/dist/lib/webdav.cjs +0 -568
  55. package/dist/lib/webdav.d.cts +0 -109
  56. package/dist/lib/webdav.d.ts +0 -109
  57. package/dist/lib/webdav.js +0 -35
  58. package/hooks/clawvault/HOOK.md +0 -83
  59. package/hooks/clawvault/handler.js +0 -879
  60. package/hooks/clawvault/handler.test.js +0 -354
package/dist/index.cjs CHANGED
@@ -30,12 +30,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- CLAWVAULT_SERVE_PATH: () => CLAWVAULT_SERVE_PATH,
34
33
  ClawVault: () => ClawVault,
35
34
  Compressor: () => Compressor,
36
35
  DEFAULT_CATEGORIES: () => DEFAULT_CATEGORIES,
37
36
  DEFAULT_CONFIG: () => DEFAULT_CONFIG,
38
- DEFAULT_SERVE_PORT: () => DEFAULT_SERVE_PORT,
39
37
  MEMORY_GRAPH_SCHEMA_VERSION: () => MEMORY_GRAPH_SCHEMA_VERSION,
40
38
  MEMORY_TYPES: () => MEMORY_TYPES,
41
39
  Observer: () => Observer,
@@ -62,19 +60,15 @@ __export(index_exports, {
62
60
  buildTemplateVariables: () => buildTemplateVariables,
63
61
  buildTransitionEvent: () => buildTransitionEvent,
64
62
  checkOpenClawCompatibility: () => checkOpenClawCompatibility,
65
- checkPeerClawVault: () => checkPeerClawVault,
66
63
  classifyQuestion: () => classifyQuestion,
67
- compareManifests: () => compareManifests,
68
64
  compatCommand: () => compatCommand,
69
65
  compatibilityExitCode: () => compatibilityExitCode,
70
66
  completeTask: () => completeTask,
71
- configureTailscaleServe: () => configureTailscaleServe,
72
67
  contextCommand: () => contextCommand,
73
68
  countBlockedTransitions: () => countBlockedTransitions,
74
69
  createProject: () => createProject,
75
70
  createVault: () => createVault,
76
71
  deterministicInjectMatches: () => deterministicInjectMatches,
77
- discoverClawVaultPeers: () => discoverClawVaultPeers,
78
72
  doctor: () => doctor,
79
73
  embedCommand: () => embedCommand,
80
74
  entitySimilarity: () => entitySimilarity,
@@ -84,34 +78,26 @@ __export(index_exports, {
84
78
  extractPreferences: () => extractPreferences,
85
79
  extractTags: () => extractTags,
86
80
  extractWikiLinks: () => extractWikiLinks,
87
- fetchRemoteFile: () => fetchRemoteFile,
88
- fetchRemoteManifest: () => fetchRemoteManifest,
89
81
  filterSuperseded: () => filterSuperseded,
90
82
  findNearestVaultPath: () => findNearestVaultPath,
91
- findPeer: () => findPeer,
92
83
  findVault: () => findVault,
93
84
  formatContextMarkdown: () => formatContextMarkdown,
94
85
  formatKanbanCard: () => formatKanbanCard,
95
86
  formatSessionRecapMarkdown: () => formatSessionRecapMarkdown,
96
87
  formatTransitionsTable: () => formatTransitionsTable,
97
88
  generateKanbanMarkdown: () => generateKanbanMarkdown,
98
- generateVaultManifest: () => generateVaultManifest,
99
89
  getConfig: () => getConfig,
100
90
  getConfigValue: () => getConfigValue,
101
91
  getMemoryGraph: () => getMemoryGraph,
102
92
  getObserverStaleness: () => getObserverStaleness,
103
- getOnlinePeers: () => getOnlinePeers,
104
93
  getProjectActivity: () => getProjectActivity,
105
94
  getProjectTasks: () => getProjectTasks,
106
95
  getScaledObservationThresholdBytes: () => getScaledObservationThresholdBytes,
107
96
  getSupersessionInfo: () => getSupersessionInfo,
108
- getTailscaleStatus: () => getTailscaleStatus,
109
- getTailscaleVersion: () => getTailscaleVersion,
110
97
  getVaultPath: () => getVaultPath,
111
98
  graphCommand: () => graphCommand,
112
99
  graphSummary: () => graphSummary,
113
100
  hasQmd: () => hasQmd,
114
- hasTailscale: () => hasTailscale,
115
101
  importKanbanBoard: () => importKanbanBoard,
116
102
  indexInjectableItems: () => indexInjectableItems,
117
103
  inferContextProfile: () => inferContextProfile,
@@ -135,7 +121,6 @@ __export(index_exports, {
135
121
  parseKanbanMarkdown: () => parseKanbanMarkdown,
136
122
  parseSessionFile: () => parseSessionFile,
137
123
  parseSessionSourceLabel: () => parseSessionSourceLabel,
138
- pushFileToRemote: () => pushFileToRemote,
139
124
  qmdEmbed: () => qmdEmbed,
140
125
  qmdUpdate: () => qmdUpdate,
141
126
  queryTransitions: () => queryTransitions,
@@ -155,11 +140,6 @@ __export(index_exports, {
155
140
  registerReflectCommand: () => registerReflectCommand,
156
141
  registerReplayCommand: () => registerReplayCommand,
157
142
  registerReweaveCommand: () => registerReweaveCommand,
158
- registerTailscaleCommands: () => registerTailscaleCommands,
159
- registerTailscaleDiscoverCommand: () => registerTailscaleDiscoverCommand,
160
- registerTailscaleServeCommand: () => registerTailscaleServeCommand,
161
- registerTailscaleStatusCommand: () => registerTailscaleStatusCommand,
162
- registerTailscaleSyncCommand: () => registerTailscaleSyncCommand,
163
143
  removeRouteRule: () => removeRouteRule,
164
144
  renderTemplate: () => renderTemplate,
165
145
  replayCommand: () => replayCommand,
@@ -167,31 +147,23 @@ __export(index_exports, {
167
147
  resetConfig: () => resetConfig,
168
148
  resolveContextProfile: () => resolveContextProfile,
169
149
  resolveLlmProvider: () => resolveLlmProvider,
170
- resolvePeerIP: () => resolvePeerIP,
171
150
  resolveVaultPath: () => resolveVaultPath,
172
151
  reweave: () => reweave,
173
152
  reweaveCommand: () => reweaveCommand,
174
153
  runPromptInjection: () => runPromptInjection,
175
154
  runReflection: () => runReflection,
176
155
  sentenceChunk: () => sentenceChunk,
177
- serveVault: () => serveVault,
178
156
  sessionRecapCommand: () => sessionRecapCommand,
179
157
  setConfigValue: () => setConfigValue,
180
158
  setupCommand: () => setupCommand,
181
- stopTailscaleServe: () => stopTailscaleServe,
182
159
  stripSupersededObservations: () => stripSupersededObservations,
183
160
  syncKanbanBoard: () => syncKanbanBoard,
184
- syncWithPeer: () => syncWithPeer,
185
- tailscaleDiscoverCommand: () => tailscaleDiscoverCommand,
186
- tailscaleServeCommand: () => tailscaleServeCommand,
187
- tailscaleStatusCommand: () => tailscaleStatusCommand,
188
- tailscaleSyncCommand: () => tailscaleSyncCommand,
189
161
  testRouteRule: () => testRouteRule,
190
162
  updateProject: () => updateProject,
191
163
  updateTask: () => updateTask
192
164
  });
193
165
  module.exports = __toCommonJS(index_exports);
194
- var fs36 = __toESM(require("fs"), 1);
166
+ var fs34 = __toESM(require("fs"), 1);
195
167
 
196
168
  // src/commands/context.ts
197
169
  var path5 = __toESM(require("path"), 1);
@@ -931,6 +903,7 @@ function stripQmdNoise(raw) {
931
903
  function parseQmdOutput(raw) {
932
904
  const trimmed = stripQmdNoise(raw).trim();
933
905
  if (!trimmed) return [];
906
+ if (trimmed.startsWith("No results") || trimmed.startsWith("No matches")) return [];
934
907
  const direct = tryParseJson(trimmed);
935
908
  const extracted = direct ? null : extractJsonPayload(trimmed);
936
909
  const parsed = direct ?? (extracted ? tryParseJson(extracted) : null);
@@ -1684,8 +1657,8 @@ function toUnresolvedNodeId(raw) {
1684
1657
  return `unresolved:${normalizeUnresolvedKey(raw)}`;
1685
1658
  }
1686
1659
  function titleFromNoteKey(noteKey) {
1687
- const basename16 = noteKey.split("/").pop() ?? noteKey;
1688
- return basename16.replace(/[-_]+/g, " ").replace(/\s+/g, " ").trim().replace(/\b\w/g, (char) => char.toUpperCase());
1660
+ const basename14 = noteKey.split("/").pop() ?? noteKey;
1661
+ return basename14.replace(/[-_]+/g, " ").replace(/\s+/g, " ").trim().replace(/\b\w/g, (char) => char.toUpperCase());
1689
1662
  }
1690
1663
  function inferNodeType(relativePath, frontmatter) {
1691
1664
  const normalized = normalizeRelativePath(relativePath).toLowerCase();
@@ -4583,9 +4556,9 @@ function collectNodeAliases(node) {
4583
4556
  aliases.add(node.title);
4584
4557
  }
4585
4558
  if (node.path) {
4586
- const basename16 = path7.basename(node.path, ".md");
4587
- aliases.add(basename16.replace(/[-_]+/g, " "));
4588
- aliases.add(basename16);
4559
+ const basename14 = path7.basename(node.path, ".md");
4560
+ aliases.add(basename14.replace(/[-_]+/g, " "));
4561
+ aliases.add(basename14);
4589
4562
  }
4590
4563
  return [...aliases].map((alias) => normalizeText(alias)).filter((alias) => alias.length >= 3);
4591
4564
  }
@@ -8258,8 +8231,8 @@ var SessionWatcher = class {
8258
8231
  this.fileOffsets.delete(resolved);
8259
8232
  this.pendingPaths.delete(resolved);
8260
8233
  });
8261
- await new Promise((resolve30, reject) => {
8262
- this.watcher?.once("ready", () => resolve30());
8234
+ await new Promise((resolve28, reject) => {
8235
+ this.watcher?.once("ready", () => resolve28());
8263
8236
  this.watcher?.once("error", (error) => reject(error));
8264
8237
  });
8265
8238
  if (this.ignoreInitial) {
@@ -9134,12 +9107,12 @@ async function watchSessions(observer, watchPath) {
9134
9107
  const watcher = new SessionWatcher(watchPath, observer);
9135
9108
  await watcher.start();
9136
9109
  console.log(`Watching session updates: ${watchPath}`);
9137
- await new Promise((resolve30) => {
9110
+ await new Promise((resolve28) => {
9138
9111
  const shutdown = async () => {
9139
9112
  process.off("SIGINT", onSigInt);
9140
9113
  process.off("SIGTERM", onSigTerm);
9141
9114
  await watcher.stop();
9142
- resolve30();
9115
+ resolve28();
9143
9116
  };
9144
9117
  const onSigInt = () => {
9145
9118
  void shutdown();
@@ -9806,2344 +9779,322 @@ function registerReweaveCommand(program) {
9806
9779
  });
9807
9780
  }
9808
9781
 
9809
- // src/commands/tailscale.ts
9810
- var path25 = __toESM(require("path"), 1);
9811
-
9812
- // src/lib/tailscale.ts
9813
- var import_child_process4 = require("child_process");
9814
- var fs25 = __toESM(require("fs"), 1);
9815
- var path24 = __toESM(require("path"), 1);
9816
- var http = __toESM(require("http"), 1);
9817
- var https = __toESM(require("https"), 1);
9782
+ // src/cli/index.ts
9783
+ function registerCliCommands(program) {
9784
+ registerContextCommand(program);
9785
+ registerInjectCommand(program);
9786
+ registerObserveCommand(program);
9787
+ registerReflectCommand(program);
9788
+ registerEmbedCommand(program);
9789
+ registerReweaveCommand(program);
9790
+ return program;
9791
+ }
9818
9792
 
9819
- // src/lib/webdav.ts
9793
+ // src/commands/setup.ts
9820
9794
  var fs24 = __toESM(require("fs"), 1);
9795
+ var os2 = __toESM(require("os"), 1);
9821
9796
  var path23 = __toESM(require("path"), 1);
9822
- var WEBDAV_PREFIX = "/webdav";
9823
- var BLOCKED_PATHS = [
9824
- ".clawvault",
9825
- ".git",
9826
- ".obsidian",
9827
- "node_modules"
9828
- ];
9829
- var SUPPORTED_METHODS = ["GET", "PUT", "DELETE", "MKCOL", "PROPFIND", "OPTIONS", "HEAD", "MOVE", "COPY"];
9830
- function toRequestSegments(requestPath) {
9831
- return requestPath.replace(/\\/g, "/").split("/").filter(Boolean);
9832
- }
9833
- function isWithinRoot(fullPath, rootPath) {
9834
- const resolvedRoot = path23.resolve(rootPath);
9835
- const relative6 = path23.relative(resolvedRoot, fullPath);
9836
- return !(relative6.startsWith("..") || path23.isAbsolute(relative6));
9837
- }
9838
- function isPathSafe(requestPath, rootPath) {
9839
- const pathParts = toRequestSegments(requestPath);
9840
- if (pathParts.includes("..")) {
9841
- return false;
9797
+ var import_child_process4 = require("child_process");
9798
+ var import_gray_matter7 = __toESM(require("gray-matter"), 1);
9799
+ var CONFIG_FILE4 = ".clawvault.json";
9800
+ function resolveVaultTarget(vaultOverride) {
9801
+ if (vaultOverride) {
9802
+ const vaultPath = path23.resolve(vaultOverride);
9803
+ return { vaultPath, source: "--vault flag", existed: fs24.existsSync(vaultPath) };
9842
9804
  }
9843
- const normalizedRelativePath = path23.normalize(pathParts.join(path23.sep));
9844
- const fullPath = path23.resolve(rootPath, normalizedRelativePath);
9845
- if (!isWithinRoot(fullPath, rootPath)) {
9846
- return false;
9805
+ const envPath = process.env.CLAWVAULT_PATH?.trim();
9806
+ const home = os2.homedir();
9807
+ if (envPath) {
9808
+ const vaultPath = path23.resolve(envPath);
9809
+ return { vaultPath, source: "CLAWVAULT_PATH", existed: fs24.existsSync(vaultPath) };
9847
9810
  }
9848
- for (const part of pathParts) {
9849
- if (BLOCKED_PATHS.includes(part)) {
9850
- return false;
9811
+ const candidates = [
9812
+ { vaultPath: path23.join(home, ".openclaw", "workspace", "memory"), source: "OpenClaw default" },
9813
+ { vaultPath: path23.resolve(process.cwd(), "memory"), source: "./memory" },
9814
+ { vaultPath: path23.join(home, "memory"), source: "~/memory" }
9815
+ ];
9816
+ for (const candidate of candidates) {
9817
+ if (fs24.existsSync(candidate.vaultPath)) {
9818
+ return { ...candidate, existed: true };
9851
9819
  }
9852
9820
  }
9853
- return true;
9821
+ const fallback = candidates[0];
9822
+ return { ...fallback, existed: false };
9854
9823
  }
9855
- function resolveWebDAVPath(requestPath, rootPath) {
9856
- const pathParts = toRequestSegments(requestPath);
9857
- if (pathParts.includes("..")) {
9858
- return null;
9859
- }
9860
- const normalizedRelativePath = path23.normalize(pathParts.join(path23.sep));
9861
- const fullPath = path23.resolve(rootPath, normalizedRelativePath);
9862
- if (!isWithinRoot(fullPath, rootPath)) {
9863
- return null;
9824
+ function ensureVaultStructure(vaultPath) {
9825
+ fs24.mkdirSync(vaultPath, { recursive: true });
9826
+ for (const category of DEFAULT_CATEGORIES) {
9827
+ fs24.mkdirSync(path23.join(vaultPath, category), { recursive: true });
9864
9828
  }
9865
- return fullPath;
9829
+ const configPath = path23.join(vaultPath, CONFIG_FILE4);
9830
+ if (fs24.existsSync(configPath)) return false;
9831
+ const now = (/* @__PURE__ */ new Date()).toISOString();
9832
+ const name = path23.basename(vaultPath);
9833
+ const meta = {
9834
+ name,
9835
+ version: "1.0.0",
9836
+ created: now,
9837
+ lastUpdated: now,
9838
+ categories: DEFAULT_CATEGORIES,
9839
+ documentCount: 0,
9840
+ qmdCollection: name,
9841
+ qmdRoot: vaultPath
9842
+ };
9843
+ fs24.writeFileSync(configPath, JSON.stringify(meta, null, 2));
9844
+ return true;
9866
9845
  }
9867
- function checkAuth(req, auth) {
9868
- if (!auth) {
9869
- return true;
9846
+ function writeBases(vaultPath, force) {
9847
+ const basesFiles = {
9848
+ "all-tasks.base": `filters:
9849
+ and:
9850
+ - file.inFolder("tasks")
9851
+ - status != "done"
9852
+ formulas:
9853
+ age: (now() - file.ctime).days
9854
+ status_icon: if(status == "blocked", "\u{1F534}", if(status == "in-progress", "\u{1F528}", if(status == "open", "\u26AA", "\u2705")))
9855
+ views:
9856
+ - type: table
9857
+ name: All Active Tasks
9858
+ groupBy:
9859
+ property: status
9860
+ direction: ASC
9861
+ order:
9862
+ - formula.status_icon
9863
+ - file.name
9864
+ - status
9865
+ - owner
9866
+ - project
9867
+ - priority
9868
+ - blocked_by
9869
+ - formula.age
9870
+ - type: cards
9871
+ name: Task Board
9872
+ groupBy:
9873
+ property: status
9874
+ direction: ASC
9875
+ order:
9876
+ - file.name
9877
+ - owner
9878
+ - project
9879
+ - priority`,
9880
+ "blocked.base": `filters:
9881
+ and:
9882
+ - file.inFolder("tasks")
9883
+ - status == "blocked"
9884
+ formulas:
9885
+ days_blocked: (now() - file.ctime).days
9886
+ views:
9887
+ - type: table
9888
+ name: Blocked Tasks
9889
+ order:
9890
+ - file.name
9891
+ - owner
9892
+ - project
9893
+ - blocked_by
9894
+ - formula.days_blocked
9895
+ - priority`,
9896
+ "by-project.base": `filters:
9897
+ and:
9898
+ - file.inFolder("tasks")
9899
+ - status != "done"
9900
+ formulas:
9901
+ status_icon: if(status == "blocked", "\u{1F534}", if(status == "in-progress", "\u{1F528}", "\u26AA"))
9902
+ views:
9903
+ - type: table
9904
+ name: By Project
9905
+ groupBy:
9906
+ property: project
9907
+ direction: ASC
9908
+ order:
9909
+ - formula.status_icon
9910
+ - file.name
9911
+ - status
9912
+ - owner
9913
+ - priority
9914
+ - type: cards
9915
+ name: Project Cards
9916
+ groupBy:
9917
+ property: project
9918
+ direction: ASC
9919
+ order:
9920
+ - file.name
9921
+ - owner
9922
+ - status`,
9923
+ "by-owner.base": `filters:
9924
+ and:
9925
+ - file.inFolder("tasks")
9926
+ - status != "done"
9927
+ views:
9928
+ - type: table
9929
+ name: By Owner
9930
+ groupBy:
9931
+ property: owner
9932
+ direction: ASC
9933
+ order:
9934
+ - file.name
9935
+ - status
9936
+ - project
9937
+ - priority`,
9938
+ "backlog.base": `filters:
9939
+ and:
9940
+ - file.inFolder("backlog")
9941
+ views:
9942
+ - type: table
9943
+ name: Backlog
9944
+ order:
9945
+ - file.name
9946
+ - source
9947
+ - project
9948
+ - file.ctime`
9949
+ };
9950
+ let written = 0;
9951
+ for (const [filename, content] of Object.entries(basesFiles)) {
9952
+ const filePath = path23.join(vaultPath, filename);
9953
+ if (force || !fs24.existsSync(filePath)) {
9954
+ fs24.writeFileSync(filePath, content);
9955
+ written++;
9956
+ }
9870
9957
  }
9871
- const authHeader = req.headers.authorization;
9872
- if (!authHeader || !authHeader.startsWith("Basic ")) {
9958
+ return written;
9959
+ }
9960
+ var NEURAL_GRAPH_CSS = `/* ClawVault Graph Colors \u2014 Neural Neural Style */
9961
+ /* Auto-generated by \`clawvault setup --theme neural\` */
9962
+
9963
+ body.theme-dark .graph-view .graph-view-container { background-color: #0a0a0a; }
9964
+
9965
+ body.theme-dark .graph-view .node.tag-person circle { fill: #00b4d8 !important; }
9966
+ body.theme-dark .graph-view .node.tag-project circle { fill: #2d6a4f !important; }
9967
+ body.theme-dark .graph-view .node.tag-decision circle { fill: #e8590c !important; }
9968
+ body.theme-dark .graph-view .node.tag-lesson circle { fill: #fcc419 !important; }
9969
+ body.theme-dark .graph-view .node.tag-commitment circle { fill: #e03131 !important; }
9970
+ body.theme-dark .graph-view .node.tag-task circle { fill: #22b8cf !important; }
9971
+ body.theme-dark .graph-view .node.tag-observation circle { fill: #7950f2 !important; }
9972
+ body.theme-dark .graph-view .node.tag-handoff circle { fill: #845ef7 !important; }
9973
+ body.theme-dark .graph-view .node.tag-daily circle { fill: #495057 !important; }
9974
+
9975
+ body.theme-dark .graph-view .node.is-focused circle {
9976
+ fill: #e8a430 !important; stroke: #e8a430 !important;
9977
+ stroke-width: 3px; filter: drop-shadow(0 0 6px #e8a430);
9978
+ }
9979
+
9980
+ body.theme-dark .graph-view .link { stroke: rgba(45, 200, 120, 0.15) !important; }
9981
+ body.theme-dark .graph-view .link.is-focused { stroke: rgba(45, 200, 120, 0.6) !important; }
9982
+ body.theme-dark .graph-view .node text { fill: #c1c2c5 !important; font-size: 0.8em; }
9983
+ `;
9984
+ var MINIMAL_GRAPH_CSS = `/* ClawVault Graph Colors \u2014 Minimal */
9985
+ /* Auto-generated by \`clawvault setup --theme minimal\` */
9986
+
9987
+ body.theme-dark .graph-view .node.tag-person circle { fill: #4a90e8 !important; }
9988
+ body.theme-dark .graph-view .node.tag-project circle { fill: #4ae85d !important; }
9989
+ body.theme-dark .graph-view .node.tag-decision circle { fill: #e85d4a !important; }
9990
+ body.theme-dark .graph-view .node.tag-lesson circle { fill: #9b59b6 !important; }
9991
+ body.theme-dark .graph-view .node.tag-task circle { fill: #e8a430 !important; }
9992
+ `;
9993
+ var NEURAL_COLOR_GROUPS = [
9994
+ { query: "path:people", color: { a: 1, rgb: 47316 } },
9995
+ { query: "path:projects", color: { a: 1, rgb: 2976335 } },
9996
+ { query: "path:decisions", color: { a: 1, rgb: 15227916 } },
9997
+ { query: "path:lessons", color: { a: 1, rgb: 16565273 } },
9998
+ { query: "path:tasks", color: { a: 1, rgb: 2275535 } },
9999
+ { query: "path:commitments", color: { a: 1, rgb: 14680369 } },
10000
+ { query: "path:backlog", color: { a: 1, rgb: 9806262 } },
10001
+ { query: "path:inbox", color: { a: 1, rgb: 15964178 } },
10002
+ { query: "path:handoffs", color: { a: 1, rgb: 8675063 } },
10003
+ { query: "path:ledger", color: { a: 1, rgb: 7950066 } }
10004
+ ];
10005
+ var MINIMAL_COLOR_GROUPS = [
10006
+ { query: "path:people", color: { a: 1, rgb: 4886760 } },
10007
+ { query: "path:projects", color: { a: 1, rgb: 4909149 } },
10008
+ { query: "path:decisions", color: { a: 1, rgb: 15228234 } },
10009
+ { query: "path:lessons", color: { a: 1, rgb: 10181046 } },
10010
+ { query: "path:tasks", color: { a: 1, rgb: 15246384 } }
10011
+ ];
10012
+ function writeGraphColors(vaultPath, theme, force) {
10013
+ const obsidianDir = path23.join(vaultPath, ".obsidian");
10014
+ if (!fs24.existsSync(obsidianDir)) {
9873
10015
  return false;
9874
10016
  }
9875
- const base64Credentials = authHeader.slice(6);
9876
- const credentials = Buffer.from(base64Credentials, "base64").toString("utf-8");
9877
- const [username, password] = credentials.split(":");
9878
- return username === auth.username && password === auth.password;
9879
- }
9880
- function escapeXml(str) {
9881
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
9882
- }
9883
- function formatWebDAVDate(date) {
9884
- return date.toUTCString();
9885
- }
9886
- function generatePropfindEntry(href, stats, isCollection) {
9887
- const resourceType = isCollection ? "<D:resourcetype><D:collection/></D:resourcetype>" : "<D:resourcetype/>";
9888
- const contentLength = stats && !isCollection ? `<D:getcontentlength>${stats.size}</D:getcontentlength>` : "";
9889
- const lastModified = stats ? `<D:getlastmodified>${formatWebDAVDate(stats.mtime)}</D:getlastmodified>` : "";
9890
- const etag = stats ? `<D:getetag>"${stats.mtime.getTime().toString(16)}-${stats.size.toString(16)}"</D:getetag>` : "";
9891
- const contentType = !isCollection ? "<D:getcontenttype>application/octet-stream</D:getcontenttype>" : "";
9892
- return ` <D:response>
9893
- <D:href>${escapeXml(href)}</D:href>
9894
- <D:propstat>
9895
- <D:prop>
9896
- ${resourceType}
9897
- ${contentLength}
9898
- ${lastModified}
9899
- ${etag}
9900
- ${contentType}
9901
- </D:prop>
9902
- <D:status>HTTP/1.1 200 OK</D:status>
9903
- </D:propstat>
9904
- </D:response>`;
9905
- }
9906
- function generatePropfindResponse(entries) {
9907
- const responseEntries = entries.map(
9908
- (e) => generatePropfindEntry(e.href, e.stats, e.isCollection)
9909
- ).join("\n");
9910
- return `<?xml version="1.0" encoding="utf-8"?>
9911
- <D:multistatus xmlns:D="DAV:">
9912
- ${responseEntries}
9913
- </D:multistatus>`;
9914
- }
9915
- function handleOptions(res, prefix) {
9916
- res.writeHead(200, {
9917
- "Allow": SUPPORTED_METHODS.join(", "),
9918
- "DAV": "1, 2",
9919
- "Content-Length": "0",
9920
- "Access-Control-Allow-Origin": "*",
9921
- "Access-Control-Allow-Methods": SUPPORTED_METHODS.join(", "),
9922
- "Access-Control-Allow-Headers": "Content-Type, Depth, Destination, Overwrite, Authorization",
9923
- "MS-Author-Via": "DAV"
9924
- });
9925
- res.end();
9926
- }
9927
- function handleHead(res, filePath) {
9928
- try {
9929
- const stats = fs24.statSync(filePath);
9930
- if (stats.isDirectory()) {
9931
- res.writeHead(200, {
9932
- "Content-Type": "httpd/unix-directory",
9933
- "Last-Modified": formatWebDAVDate(stats.mtime),
9934
- "ETag": `"${stats.mtime.getTime().toString(16)}"`,
9935
- "Access-Control-Allow-Origin": "*"
9936
- });
9937
- } else {
9938
- res.writeHead(200, {
9939
- "Content-Type": "application/octet-stream",
9940
- "Content-Length": stats.size.toString(),
9941
- "Last-Modified": formatWebDAVDate(stats.mtime),
9942
- "ETag": `"${stats.mtime.getTime().toString(16)}-${stats.size.toString(16)}"`,
9943
- "Access-Control-Allow-Origin": "*"
9944
- });
9945
- }
9946
- res.end();
9947
- } catch (err) {
9948
- res.writeHead(404, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
9949
- res.end("Not Found");
10017
+ const snippetsDir = path23.join(obsidianDir, "snippets");
10018
+ fs24.mkdirSync(snippetsDir, { recursive: true });
10019
+ const snippetName = "clawvault-graph";
10020
+ const snippetPath = path23.join(snippetsDir, `${snippetName}.css`);
10021
+ if (!force && fs24.existsSync(snippetPath)) {
10022
+ return false;
9950
10023
  }
9951
- }
9952
- function handleGet(res, filePath) {
9953
- try {
9954
- const stats = fs24.statSync(filePath);
9955
- if (stats.isDirectory()) {
9956
- const entries = fs24.readdirSync(filePath);
9957
- const listing = entries.join("\n");
9958
- res.writeHead(200, {
9959
- "Content-Type": "text/plain",
9960
- "Content-Length": Buffer.byteLength(listing).toString(),
9961
- "Access-Control-Allow-Origin": "*"
9962
- });
9963
- res.end(listing);
9964
- } else {
9965
- const content = fs24.readFileSync(filePath);
9966
- res.writeHead(200, {
9967
- "Content-Type": "application/octet-stream",
9968
- "Content-Length": content.length.toString(),
9969
- "Last-Modified": formatWebDAVDate(stats.mtime),
9970
- "ETag": `"${stats.mtime.getTime().toString(16)}-${stats.size.toString(16)}"`,
9971
- "Access-Control-Allow-Origin": "*"
9972
- });
9973
- res.end(content);
10024
+ const css = theme === "neural" ? NEURAL_GRAPH_CSS : MINIMAL_GRAPH_CSS;
10025
+ fs24.writeFileSync(snippetPath, css);
10026
+ const appearancePath = path23.join(obsidianDir, "appearance.json");
10027
+ let appearance = {};
10028
+ if (fs24.existsSync(appearancePath)) {
10029
+ try {
10030
+ appearance = JSON.parse(fs24.readFileSync(appearancePath, "utf-8"));
10031
+ } catch {
9974
10032
  }
9975
- } catch (err) {
9976
- res.writeHead(404, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
9977
- res.end("Not Found");
9978
10033
  }
9979
- }
9980
- function handlePut(res, filePath, body) {
9981
- try {
9982
- const exists = fs24.existsSync(filePath);
9983
- const dir = path23.dirname(filePath);
9984
- if (!fs24.existsSync(dir)) {
9985
- fs24.mkdirSync(dir, { recursive: true });
9986
- }
9987
- fs24.writeFileSync(filePath, body);
9988
- const status = exists ? 204 : 201;
9989
- res.writeHead(status, {
9990
- "Content-Length": "0",
9991
- "Access-Control-Allow-Origin": "*"
9992
- });
9993
- res.end();
9994
- } catch (err) {
9995
- res.writeHead(500, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
9996
- res.end(`Error: ${err}`);
10034
+ const snippets = appearance.enabledCssSnippets || [];
10035
+ if (!snippets.includes(snippetName)) {
10036
+ snippets.push(snippetName);
10037
+ appearance.enabledCssSnippets = snippets;
10038
+ fs24.writeFileSync(appearancePath, JSON.stringify(appearance, null, 2));
9997
10039
  }
9998
- }
9999
- function handleDelete(res, filePath) {
10000
- try {
10001
- if (!fs24.existsSync(filePath)) {
10002
- res.writeHead(404, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10003
- res.end("Not Found");
10004
- return;
10005
- }
10006
- const stats = fs24.statSync(filePath);
10007
- if (stats.isDirectory()) {
10008
- fs24.rmSync(filePath, { recursive: true });
10009
- } else {
10010
- fs24.unlinkSync(filePath);
10011
- }
10012
- res.writeHead(204, {
10013
- "Content-Length": "0",
10014
- "Access-Control-Allow-Origin": "*"
10015
- });
10016
- res.end();
10017
- } catch (err) {
10018
- res.writeHead(500, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10019
- res.end(`Error: ${err}`);
10020
- }
10021
- }
10022
- function handleMkcol(res, filePath) {
10023
- try {
10024
- if (fs24.existsSync(filePath)) {
10025
- res.writeHead(405, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10026
- res.end("Resource already exists");
10027
- return;
10028
- }
10029
- const parent = path23.dirname(filePath);
10030
- if (!fs24.existsSync(parent)) {
10031
- res.writeHead(409, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10032
- res.end("Parent directory does not exist");
10033
- return;
10034
- }
10035
- fs24.mkdirSync(filePath);
10036
- res.writeHead(201, {
10037
- "Content-Length": "0",
10038
- "Access-Control-Allow-Origin": "*"
10039
- });
10040
- res.end();
10041
- } catch (err) {
10042
- res.writeHead(500, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10043
- res.end(`Error: ${err}`);
10044
- }
10045
- }
10046
- function handlePropfind(res, filePath, webdavPath, prefix, depth) {
10047
- try {
10048
- if (!fs24.existsSync(filePath)) {
10049
- res.writeHead(404, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10050
- res.end("Not Found");
10051
- return;
10052
- }
10053
- const stats = fs24.statSync(filePath);
10054
- const entries = [];
10055
- const normalizedWebdavPath = webdavPath.startsWith("/") ? webdavPath : "/" + webdavPath;
10056
- const href = prefix + normalizedWebdavPath;
10057
- entries.push({
10058
- href: href.endsWith("/") || stats.isDirectory() ? href : href,
10059
- stats,
10060
- isCollection: stats.isDirectory()
10061
- });
10062
- if (stats.isDirectory() && depth !== "0") {
10063
- try {
10064
- const children = fs24.readdirSync(filePath);
10065
- for (const child of children) {
10066
- if (BLOCKED_PATHS.includes(child)) {
10067
- continue;
10068
- }
10069
- const childPath = path23.join(filePath, child);
10070
- const childWebdavPath = normalizedWebdavPath.endsWith("/") ? normalizedWebdavPath + child : normalizedWebdavPath + "/" + child;
10071
- try {
10072
- const childStats = fs24.statSync(childPath);
10073
- entries.push({
10074
- href: prefix + childWebdavPath,
10075
- stats: childStats,
10076
- isCollection: childStats.isDirectory()
10077
- });
10078
- } catch {
10079
- }
10080
- }
10081
- } catch {
10082
- }
10083
- }
10084
- const xml = generatePropfindResponse(entries);
10085
- res.writeHead(207, {
10086
- "Content-Type": "application/xml; charset=utf-8",
10087
- "Content-Length": Buffer.byteLength(xml).toString(),
10088
- "Access-Control-Allow-Origin": "*"
10089
- });
10090
- res.end(xml);
10091
- } catch (err) {
10092
- res.writeHead(500, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10093
- res.end(`Error: ${err}`);
10094
- }
10095
- }
10096
- function handleMove(res, sourcePath, destinationPath, overwrite) {
10097
- try {
10098
- if (!fs24.existsSync(sourcePath)) {
10099
- res.writeHead(404, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10100
- res.end("Source not found");
10101
- return;
10102
- }
10103
- if (!destinationPath) {
10104
- res.writeHead(400, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10105
- res.end("Destination header required");
10106
- return;
10107
- }
10108
- const destExists = fs24.existsSync(destinationPath);
10109
- if (destExists && !overwrite) {
10110
- res.writeHead(412, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10111
- res.end("Destination exists and Overwrite is F");
10112
- return;
10113
- }
10114
- const destDir = path23.dirname(destinationPath);
10115
- if (!fs24.existsSync(destDir)) {
10116
- fs24.mkdirSync(destDir, { recursive: true });
10117
- }
10118
- if (destExists) {
10119
- const destStats = fs24.statSync(destinationPath);
10120
- if (destStats.isDirectory()) {
10121
- fs24.rmSync(destinationPath, { recursive: true });
10122
- } else {
10123
- fs24.unlinkSync(destinationPath);
10124
- }
10125
- }
10126
- fs24.renameSync(sourcePath, destinationPath);
10127
- const status = destExists ? 204 : 201;
10128
- res.writeHead(status, {
10129
- "Content-Length": "0",
10130
- "Access-Control-Allow-Origin": "*"
10131
- });
10132
- res.end();
10133
- } catch (err) {
10134
- res.writeHead(500, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10135
- res.end(`Error: ${err}`);
10136
- }
10137
- }
10138
- function handleCopy(res, sourcePath, destinationPath, overwrite) {
10139
- try {
10140
- if (!fs24.existsSync(sourcePath)) {
10141
- res.writeHead(404, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10142
- res.end("Source not found");
10143
- return;
10144
- }
10145
- if (!destinationPath) {
10146
- res.writeHead(400, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10147
- res.end("Destination header required");
10148
- return;
10149
- }
10150
- const destExists = fs24.existsSync(destinationPath);
10151
- if (destExists && !overwrite) {
10152
- res.writeHead(412, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10153
- res.end("Destination exists and Overwrite is F");
10154
- return;
10155
- }
10156
- const destDir = path23.dirname(destinationPath);
10157
- if (!fs24.existsSync(destDir)) {
10158
- fs24.mkdirSync(destDir, { recursive: true });
10159
- }
10160
- const sourceStats = fs24.statSync(sourcePath);
10161
- if (sourceStats.isDirectory()) {
10162
- copyDirRecursive(sourcePath, destinationPath);
10163
- } else {
10164
- fs24.copyFileSync(sourcePath, destinationPath);
10165
- }
10166
- const status = destExists ? 204 : 201;
10167
- res.writeHead(status, {
10168
- "Content-Length": "0",
10169
- "Access-Control-Allow-Origin": "*"
10170
- });
10171
- res.end();
10172
- } catch (err) {
10173
- res.writeHead(500, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10174
- res.end(`Error: ${err}`);
10175
- }
10176
- }
10177
- function copyDirRecursive(src, dest) {
10178
- if (!fs24.existsSync(dest)) {
10179
- fs24.mkdirSync(dest, { recursive: true });
10180
- }
10181
- const entries = fs24.readdirSync(src, { withFileTypes: true });
10182
- for (const entry of entries) {
10183
- const srcPath = path23.join(src, entry.name);
10184
- const destPath = path23.join(dest, entry.name);
10185
- if (entry.isDirectory()) {
10186
- copyDirRecursive(srcPath, destPath);
10187
- } else {
10188
- fs24.copyFileSync(srcPath, destPath);
10189
- }
10190
- }
10191
- }
10192
- function parseDestinationHeader(destinationHeader, prefix, rootPath) {
10193
- if (!destinationHeader) {
10194
- return null;
10195
- }
10196
- try {
10197
- let destPath;
10198
- if (destinationHeader.startsWith("http://") || destinationHeader.startsWith("https://")) {
10199
- const url = new URL(destinationHeader);
10200
- destPath = decodeURIComponent(url.pathname);
10201
- } else {
10202
- destPath = decodeURIComponent(destinationHeader);
10203
- }
10204
- if (destPath.startsWith(prefix)) {
10205
- destPath = destPath.slice(prefix.length);
10206
- }
10207
- return resolveWebDAVPath(destPath, rootPath);
10208
- } catch {
10209
- return null;
10210
- }
10211
- }
10212
- function createWebDAVHandler(config) {
10213
- const { rootPath, prefix = WEBDAV_PREFIX, auth } = config;
10214
- return async (req, res) => {
10215
- const rawUrl = req.url || "/";
10216
- if (rawUrl.includes("..")) {
10217
- if (rawUrl.startsWith(prefix)) {
10218
- res.writeHead(403, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10219
- res.end("Forbidden");
10220
- return true;
10221
- }
10222
- }
10223
- const url = new URL(rawUrl, `http://${req.headers.host || "localhost"}`);
10224
- const pathname = decodeURIComponent(url.pathname);
10225
- if (!pathname.startsWith(prefix)) {
10226
- return false;
10227
- }
10228
- let webdavPath = pathname.slice(prefix.length);
10229
- if (!webdavPath.startsWith("/")) {
10230
- webdavPath = "/" + webdavPath;
10231
- }
10232
- if (req.method === "OPTIONS") {
10233
- handleOptions(res, prefix);
10234
- return true;
10235
- }
10236
- if (!checkAuth(req, auth)) {
10237
- res.writeHead(401, {
10238
- "WWW-Authenticate": 'Basic realm="ClawVault WebDAV"',
10239
- "Content-Type": "text/plain",
10240
- "Access-Control-Allow-Origin": "*"
10241
- });
10242
- res.end("Unauthorized");
10243
- return true;
10244
- }
10245
- if (!isPathSafe(webdavPath, rootPath)) {
10246
- res.writeHead(403, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10247
- res.end("Forbidden");
10248
- return true;
10249
- }
10250
- const filePath = resolveWebDAVPath(webdavPath, rootPath);
10251
- if (!filePath) {
10252
- res.writeHead(403, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10253
- res.end("Forbidden");
10254
- return true;
10255
- }
10256
- const depth = req.headers.depth || "infinity";
10257
- const overwrite = req.headers.overwrite?.toUpperCase() !== "F";
10258
- const destinationHeader = req.headers.destination;
10259
- switch (req.method) {
10260
- case "HEAD":
10261
- handleHead(res, filePath);
10262
- return true;
10263
- case "GET":
10264
- handleGet(res, filePath);
10265
- return true;
10266
- case "PUT": {
10267
- const chunks = [];
10268
- for await (const chunk of req) {
10269
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
10270
- }
10271
- const body = Buffer.concat(chunks);
10272
- handlePut(res, filePath, body);
10273
- return true;
10274
- }
10275
- case "DELETE":
10276
- handleDelete(res, filePath);
10277
- return true;
10278
- case "MKCOL":
10279
- handleMkcol(res, filePath);
10280
- return true;
10281
- case "PROPFIND":
10282
- handlePropfind(res, filePath, webdavPath, prefix, depth);
10283
- return true;
10284
- case "MOVE": {
10285
- const destPath = parseDestinationHeader(destinationHeader, prefix, rootPath);
10286
- if (destPath && destinationHeader) {
10287
- const destWebdavPath = destinationHeader.includes(prefix) ? destinationHeader.slice(destinationHeader.indexOf(prefix) + prefix.length) : destinationHeader;
10288
- if (!isPathSafe(destWebdavPath, rootPath)) {
10289
- res.writeHead(403, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10290
- res.end("Forbidden");
10291
- return true;
10292
- }
10293
- }
10294
- handleMove(res, filePath, destPath, overwrite);
10295
- return true;
10296
- }
10297
- case "COPY": {
10298
- const destPath = parseDestinationHeader(destinationHeader, prefix, rootPath);
10299
- if (destPath && destinationHeader) {
10300
- const destWebdavPath = destinationHeader.includes(prefix) ? destinationHeader.slice(destinationHeader.indexOf(prefix) + prefix.length) : destinationHeader;
10301
- if (!isPathSafe(destWebdavPath, rootPath)) {
10302
- res.writeHead(403, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10303
- res.end("Forbidden");
10304
- return true;
10305
- }
10306
- }
10307
- handleCopy(res, filePath, destPath, overwrite);
10308
- return true;
10309
- }
10310
- default:
10311
- res.writeHead(405, {
10312
- "Allow": SUPPORTED_METHODS.join(", "),
10313
- "Content-Type": "text/plain",
10314
- "Access-Control-Allow-Origin": "*"
10315
- });
10316
- res.end("Method Not Allowed");
10317
- return true;
10318
- }
10319
- };
10320
- }
10321
-
10322
- // src/lib/tailscale.ts
10323
- var crypto = __toESM(require("crypto"), 1);
10324
- var DEFAULT_SERVE_PORT = 8384;
10325
- var CLAWVAULT_SERVE_PATH = "/.clawvault";
10326
- function hasTailscale() {
10327
- const probe = (0, import_child_process4.spawnSync)("tailscale", ["version"], {
10328
- stdio: "pipe",
10329
- encoding: "utf-8",
10330
- timeout: 5e3
10331
- });
10332
- return !probe.error && probe.status === 0;
10333
- }
10334
- function getTailscaleVersion() {
10335
- const result = (0, import_child_process4.spawnSync)("tailscale", ["version"], {
10336
- stdio: "pipe",
10337
- encoding: "utf-8",
10338
- timeout: 5e3
10339
- });
10340
- if (result.error || result.status !== 0) {
10341
- return null;
10342
- }
10343
- const lines = result.stdout.trim().split("\n");
10344
- return lines[0] || null;
10345
- }
10346
- function getTailscaleStatus() {
10347
- const status = {
10348
- installed: false,
10349
- running: false,
10350
- connected: false,
10351
- peers: []
10352
- };
10353
- if (!hasTailscale()) {
10354
- status.error = "Tailscale CLI not found. Install from https://tailscale.com/download";
10355
- return status;
10356
- }
10357
- status.installed = true;
10358
- const result = (0, import_child_process4.spawnSync)("tailscale", ["status", "--json"], {
10359
- stdio: "pipe",
10360
- encoding: "utf-8",
10361
- timeout: 1e4
10362
- });
10363
- if (result.error) {
10364
- status.error = `Failed to get Tailscale status: ${result.error.message}`;
10365
- return status;
10366
- }
10367
- if (result.status !== 0) {
10368
- status.error = result.stderr?.trim() || "Tailscale daemon not running";
10369
- return status;
10370
- }
10371
- try {
10372
- const data = JSON.parse(result.stdout);
10373
- status.running = true;
10374
- status.backendState = data.BackendState;
10375
- status.connected = data.BackendState === "Running";
10376
- status.tailnetName = data.CurrentTailnet?.Name;
10377
- if (data.Self) {
10378
- status.selfIP = data.Self.TailscaleIPs?.[0];
10379
- status.selfHostname = data.Self.HostName;
10380
- status.selfDNSName = data.Self.DNSName;
10381
- }
10382
- if (data.Peer) {
10383
- for (const [_, peerData] of Object.entries(data.Peer)) {
10384
- const peer = {
10385
- hostname: peerData.HostName || "",
10386
- dnsName: peerData.DNSName || "",
10387
- tailscaleIPs: peerData.TailscaleIPs || [],
10388
- online: peerData.Online || false,
10389
- os: peerData.OS,
10390
- exitNode: peerData.ExitNode,
10391
- tags: peerData.Tags,
10392
- lastSeen: peerData.LastSeen
10393
- };
10394
- status.peers.push(peer);
10395
- }
10396
- }
10397
- } catch (err) {
10398
- status.error = `Failed to parse Tailscale status: ${err}`;
10399
- }
10400
- return status;
10401
- }
10402
- function findPeer(hostname) {
10403
- const status = getTailscaleStatus();
10404
- if (!status.connected) {
10405
- return null;
10406
- }
10407
- const normalizedSearch = hostname.toLowerCase();
10408
- let peer = status.peers.find(
10409
- (p) => p.hostname.toLowerCase() === normalizedSearch
10410
- );
10411
- if (peer) return peer;
10412
- peer = status.peers.find(
10413
- (p) => p.dnsName.toLowerCase().startsWith(normalizedSearch)
10414
- );
10415
- if (peer) return peer;
10416
- peer = status.peers.find(
10417
- (p) => p.hostname.toLowerCase().includes(normalizedSearch)
10418
- );
10419
- return peer || null;
10420
- }
10421
- function getOnlinePeers() {
10422
- const status = getTailscaleStatus();
10423
- return status.peers.filter((p) => p.online);
10424
- }
10425
- function resolvePeerIP(hostname) {
10426
- const peer = findPeer(hostname);
10427
- return peer?.tailscaleIPs[0] || null;
10428
- }
10429
- function calculateChecksum(filePath) {
10430
- const content = fs25.readFileSync(filePath);
10431
- return crypto.createHash("sha256").update(content).digest("hex");
10432
- }
10433
- function generateVaultManifest(vaultPath) {
10434
- const configPath = path24.join(vaultPath, ".clawvault.json");
10435
- if (!fs25.existsSync(configPath)) {
10436
- throw new Error(`Not a ClawVault: ${vaultPath}`);
10437
- }
10438
- const config = JSON.parse(fs25.readFileSync(configPath, "utf-8"));
10439
- const files = [];
10440
- function walkDir(dir, relativePath = "") {
10441
- const entries = fs25.readdirSync(dir, { withFileTypes: true });
10442
- for (const entry of entries) {
10443
- const fullPath = path24.join(dir, entry.name);
10444
- const relPath = path24.join(relativePath, entry.name);
10445
- if (entry.name.startsWith(".") && entry.name !== ".clawvault.json") {
10446
- continue;
10447
- }
10448
- if (entry.name === "node_modules") {
10449
- continue;
10450
- }
10451
- if (entry.isDirectory()) {
10452
- walkDir(fullPath, relPath);
10453
- } else if (entry.isFile() && (entry.name.endsWith(".md") || entry.name === ".clawvault.json")) {
10454
- const stats = fs25.statSync(fullPath);
10455
- const category = relativePath.split(path24.sep)[0] || "root";
10456
- files.push({
10457
- path: relPath,
10458
- size: stats.size,
10459
- modified: stats.mtime.toISOString(),
10460
- checksum: calculateChecksum(fullPath),
10461
- category
10462
- });
10463
- }
10464
- }
10465
- }
10466
- walkDir(vaultPath);
10467
- return {
10468
- name: config.name,
10469
- version: config.version || "1.0.0",
10470
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
10471
- files
10472
- };
10473
- }
10474
- function compareManifests(local, remote) {
10475
- const localFiles = new Map(local.files.map((f) => [f.path, f]));
10476
- const remoteFiles = new Map(remote.files.map((f) => [f.path, f]));
10477
- const toPush = [];
10478
- const toPull = [];
10479
- const conflicts = [];
10480
- const unchanged = [];
10481
- for (const [filePath, localFile] of localFiles) {
10482
- const remoteFile = remoteFiles.get(filePath);
10483
- if (!remoteFile) {
10484
- toPush.push(localFile);
10485
- } else if (localFile.checksum === remoteFile.checksum) {
10486
- unchanged.push(filePath);
10487
- } else {
10488
- const localTime = new Date(localFile.modified).getTime();
10489
- const remoteTime = new Date(remoteFile.modified).getTime();
10490
- if (localTime > remoteTime) {
10491
- toPush.push(localFile);
10492
- } else if (remoteTime > localTime) {
10493
- toPull.push(remoteFile);
10494
- } else {
10495
- conflicts.push({ path: filePath, local: localFile, remote: remoteFile });
10496
- }
10497
- }
10498
- }
10499
- for (const [filePath, remoteFile] of remoteFiles) {
10500
- if (!localFiles.has(filePath)) {
10501
- toPull.push(remoteFile);
10502
- }
10503
- }
10504
- return { toPush, toPull, conflicts, unchanged };
10505
- }
10506
- function serveVault(vaultPath, options = {}) {
10507
- const port = options.port || DEFAULT_SERVE_PORT;
10508
- const pathPrefix = options.pathPrefix || CLAWVAULT_SERVE_PATH;
10509
- if (!fs25.existsSync(path24.join(vaultPath, ".clawvault.json"))) {
10510
- throw new Error(`Not a ClawVault: ${vaultPath}`);
10511
- }
10512
- const webdavHandler = createWebDAVHandler({
10513
- rootPath: vaultPath,
10514
- prefix: WEBDAV_PREFIX,
10515
- auth: options.webdavAuth
10516
- });
10517
- const server = http.createServer(async (req, res) => {
10518
- const url = new URL(req.url || "/", `http://localhost:${port}`);
10519
- const pathname = url.pathname;
10520
- if (pathname.startsWith(WEBDAV_PREFIX)) {
10521
- try {
10522
- const handled = await webdavHandler(req, res);
10523
- if (handled) return;
10524
- } catch (err) {
10525
- res.writeHead(500, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
10526
- res.end(`WebDAV Error: ${err}`);
10527
- return;
10528
- }
10529
- }
10530
- res.setHeader("Access-Control-Allow-Origin", "*");
10531
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
10532
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
10533
- if (req.method === "OPTIONS") {
10534
- res.writeHead(200);
10535
- res.end();
10536
- return;
10537
- }
10538
- if (pathname === `${pathPrefix}/health`) {
10539
- res.writeHead(200, { "Content-Type": "application/json" });
10540
- res.end(JSON.stringify({ status: "ok", vault: path24.basename(vaultPath) }));
10541
- return;
10542
- }
10543
- if (pathname === `${pathPrefix}/manifest`) {
10544
- try {
10545
- const manifest = generateVaultManifest(vaultPath);
10546
- res.writeHead(200, { "Content-Type": "application/json" });
10547
- res.end(JSON.stringify(manifest));
10548
- } catch (err) {
10549
- res.writeHead(500, { "Content-Type": "application/json" });
10550
- res.end(JSON.stringify({ error: String(err) }));
10551
- }
10552
- return;
10553
- }
10554
- if (pathname.startsWith(`${pathPrefix}/files/`)) {
10555
- const relativePath = decodeURIComponent(pathname.slice(`${pathPrefix}/files/`.length));
10556
- const filePath = path24.join(vaultPath, relativePath);
10557
- const resolvedPath = path24.resolve(filePath);
10558
- const resolvedVault = path24.resolve(vaultPath);
10559
- if (!resolvedPath.startsWith(resolvedVault)) {
10560
- res.writeHead(403, { "Content-Type": "application/json" });
10561
- res.end(JSON.stringify({ error: "Access denied" }));
10562
- return;
10563
- }
10564
- if (!fs25.existsSync(filePath)) {
10565
- res.writeHead(404, { "Content-Type": "application/json" });
10566
- res.end(JSON.stringify({ error: "File not found" }));
10567
- return;
10568
- }
10569
- try {
10570
- const content = fs25.readFileSync(filePath, "utf-8");
10571
- const stats = fs25.statSync(filePath);
10572
- res.writeHead(200, {
10573
- "Content-Type": "text/markdown",
10574
- "Content-Length": Buffer.byteLength(content),
10575
- "Last-Modified": stats.mtime.toUTCString()
10576
- });
10577
- res.end(content);
10578
- } catch (err) {
10579
- res.writeHead(500, { "Content-Type": "application/json" });
10580
- res.end(JSON.stringify({ error: String(err) }));
10581
- }
10582
- return;
10583
- }
10584
- if (pathname.startsWith(`${pathPrefix}/upload/`) && req.method === "POST") {
10585
- const relativePath = decodeURIComponent(pathname.slice(`${pathPrefix}/upload/`.length));
10586
- const filePath = path24.join(vaultPath, relativePath);
10587
- const resolvedPath = path24.resolve(filePath);
10588
- const resolvedVault = path24.resolve(vaultPath);
10589
- if (!resolvedPath.startsWith(resolvedVault)) {
10590
- res.writeHead(403, { "Content-Type": "application/json" });
10591
- res.end(JSON.stringify({ error: "Access denied" }));
10592
- return;
10593
- }
10594
- let body = "";
10595
- req.on("data", (chunk) => {
10596
- body += chunk;
10597
- });
10598
- req.on("end", () => {
10599
- try {
10600
- const dir = path24.dirname(filePath);
10601
- if (!fs25.existsSync(dir)) {
10602
- fs25.mkdirSync(dir, { recursive: true });
10603
- }
10604
- fs25.writeFileSync(filePath, body, "utf-8");
10605
- res.writeHead(200, { "Content-Type": "application/json" });
10606
- res.end(JSON.stringify({ success: true, path: relativePath }));
10607
- } catch (err) {
10608
- res.writeHead(500, { "Content-Type": "application/json" });
10609
- res.end(JSON.stringify({ error: String(err) }));
10610
- }
10611
- });
10612
- return;
10613
- }
10614
- if (pathname === pathPrefix || pathname === `${pathPrefix}/`) {
10615
- res.writeHead(200, { "Content-Type": "application/json" });
10616
- res.end(JSON.stringify({
10617
- service: "clawvault-sync",
10618
- version: "1.0.0",
10619
- vault: path24.basename(vaultPath),
10620
- endpoints: {
10621
- health: `${pathPrefix}/health`,
10622
- manifest: `${pathPrefix}/manifest`,
10623
- files: `${pathPrefix}/files/<path>`,
10624
- upload: `${pathPrefix}/upload/<path>`,
10625
- webdav: `${WEBDAV_PREFIX}/`
10626
- }
10627
- }));
10628
- return;
10629
- }
10630
- res.writeHead(404, { "Content-Type": "application/json" });
10631
- res.end(JSON.stringify({ error: "Not found" }));
10632
- });
10633
- server.listen(port, "0.0.0.0");
10634
- return {
10635
- server,
10636
- port,
10637
- stop: () => new Promise((resolve30, reject) => {
10638
- server.close((err) => {
10639
- if (err) reject(err);
10640
- else resolve30();
10641
- });
10642
- })
10643
- };
10644
- }
10645
- async function fetchRemoteManifest(host, port = DEFAULT_SERVE_PORT, useHttps = false) {
10646
- return new Promise((resolve30, reject) => {
10647
- const protocol = useHttps ? https : http;
10648
- const url = `${useHttps ? "https" : "http"}://${host}:${port}${CLAWVAULT_SERVE_PATH}/manifest`;
10649
- const req = protocol.get(url, { timeout: 1e4 }, (res) => {
10650
- let data = "";
10651
- res.on("data", (chunk) => {
10652
- data += chunk;
10653
- });
10654
- res.on("end", () => {
10655
- if (res.statusCode !== 200) {
10656
- reject(new Error(`Failed to fetch manifest: HTTP ${res.statusCode}`));
10657
- return;
10658
- }
10659
- try {
10660
- resolve30(JSON.parse(data));
10661
- } catch (err) {
10662
- reject(new Error(`Invalid manifest response: ${err}`));
10663
- }
10664
- });
10665
- });
10666
- req.on("error", reject);
10667
- req.on("timeout", () => {
10668
- req.destroy();
10669
- reject(new Error("Request timed out"));
10670
- });
10671
- });
10672
- }
10673
- async function fetchRemoteFile(host, filePath, port = DEFAULT_SERVE_PORT, useHttps = false) {
10674
- return new Promise((resolve30, reject) => {
10675
- const protocol = useHttps ? https : http;
10676
- const encodedPath = encodeURIComponent(filePath).replace(/%2F/g, "/");
10677
- const url = `${useHttps ? "https" : "http"}://${host}:${port}${CLAWVAULT_SERVE_PATH}/files/${encodedPath}`;
10678
- const req = protocol.get(url, { timeout: 3e4 }, (res) => {
10679
- let data = "";
10680
- res.on("data", (chunk) => {
10681
- data += chunk;
10682
- });
10683
- res.on("end", () => {
10684
- if (res.statusCode !== 200) {
10685
- reject(new Error(`Failed to fetch file: HTTP ${res.statusCode}`));
10686
- return;
10687
- }
10688
- resolve30(data);
10689
- });
10690
- });
10691
- req.on("error", reject);
10692
- req.on("timeout", () => {
10693
- req.destroy();
10694
- reject(new Error("Request timed out"));
10695
- });
10696
- });
10697
- }
10698
- async function pushFileToRemote(host, filePath, content, port = DEFAULT_SERVE_PORT, useHttps = false) {
10699
- return new Promise((resolve30, reject) => {
10700
- const protocol = useHttps ? https : http;
10701
- const encodedPath = encodeURIComponent(filePath).replace(/%2F/g, "/");
10702
- const url = new URL(`${useHttps ? "https" : "http"}://${host}:${port}${CLAWVAULT_SERVE_PATH}/upload/${encodedPath}`);
10703
- const options = {
10704
- hostname: url.hostname,
10705
- port: url.port,
10706
- path: url.pathname,
10707
- method: "POST",
10708
- headers: {
10709
- "Content-Type": "text/markdown",
10710
- "Content-Length": Buffer.byteLength(content)
10711
- },
10712
- timeout: 3e4
10713
- };
10714
- const req = protocol.request(options, (res) => {
10715
- let data = "";
10716
- res.on("data", (chunk) => {
10717
- data += chunk;
10718
- });
10719
- res.on("end", () => {
10720
- if (res.statusCode !== 200) {
10721
- reject(new Error(`Failed to push file: HTTP ${res.statusCode}`));
10722
- return;
10723
- }
10724
- resolve30();
10725
- });
10726
- });
10727
- req.on("error", reject);
10728
- req.on("timeout", () => {
10729
- req.destroy();
10730
- reject(new Error("Request timed out"));
10731
- });
10732
- req.write(content);
10733
- req.end();
10734
- });
10735
- }
10736
- async function syncWithPeer(vaultPath, options) {
10737
- const startTime = Date.now();
10738
- const result = {
10739
- pushed: [],
10740
- pulled: [],
10741
- deleted: [],
10742
- unchanged: [],
10743
- errors: [],
10744
- stats: {
10745
- bytesTransferred: 0,
10746
- filesProcessed: 0,
10747
- duration: 0
10748
- }
10749
- };
10750
- const {
10751
- peer,
10752
- port = DEFAULT_SERVE_PORT,
10753
- direction = "bidirectional",
10754
- dryRun = false,
10755
- deleteOrphans = false,
10756
- categories,
10757
- https: useHttps = false
10758
- } = options;
10759
- let host = peer;
10760
- if (!peer.match(/^\d+\.\d+\.\d+\.\d+$/)) {
10761
- const resolvedIP = resolvePeerIP(peer);
10762
- if (!resolvedIP) {
10763
- result.errors.push(`Could not resolve peer: ${peer}`);
10764
- result.stats.duration = Date.now() - startTime;
10765
- return result;
10766
- }
10767
- host = resolvedIP;
10768
- }
10769
- try {
10770
- const localManifest = generateVaultManifest(vaultPath);
10771
- const remoteManifest = await fetchRemoteManifest(host, port, useHttps);
10772
- let { toPush, toPull, conflicts, unchanged } = compareManifests(localManifest, remoteManifest);
10773
- if (categories && categories.length > 0) {
10774
- const categorySet = new Set(categories);
10775
- toPush = toPush.filter((f) => categorySet.has(f.category));
10776
- toPull = toPull.filter((f) => categorySet.has(f.category));
10777
- }
10778
- result.unchanged = unchanged;
10779
- for (const conflict of conflicts) {
10780
- result.errors.push(`Conflict: ${conflict.path} (local and remote have same timestamp but different content)`);
10781
- }
10782
- if (direction === "push" || direction === "bidirectional") {
10783
- for (const file of toPush) {
10784
- try {
10785
- if (!dryRun) {
10786
- const content = fs25.readFileSync(path24.join(vaultPath, file.path), "utf-8");
10787
- await pushFileToRemote(host, file.path, content, port, useHttps);
10788
- result.stats.bytesTransferred += file.size;
10789
- }
10790
- result.pushed.push(file.path);
10791
- result.stats.filesProcessed++;
10792
- } catch (err) {
10793
- result.errors.push(`Failed to push ${file.path}: ${err}`);
10794
- }
10795
- }
10796
- }
10797
- if (direction === "pull" || direction === "bidirectional") {
10798
- for (const file of toPull) {
10799
- try {
10800
- if (!dryRun) {
10801
- const content = await fetchRemoteFile(host, file.path, port, useHttps);
10802
- const filePath = path24.join(vaultPath, file.path);
10803
- const dir = path24.dirname(filePath);
10804
- if (!fs25.existsSync(dir)) {
10805
- fs25.mkdirSync(dir, { recursive: true });
10806
- }
10807
- fs25.writeFileSync(filePath, content, "utf-8");
10808
- result.stats.bytesTransferred += file.size;
10809
- }
10810
- result.pulled.push(file.path);
10811
- result.stats.filesProcessed++;
10812
- } catch (err) {
10813
- result.errors.push(`Failed to pull ${file.path}: ${err}`);
10814
- }
10815
- }
10816
- }
10817
- if (deleteOrphans && direction === "pull") {
10818
- const remoteFiles = new Set(remoteManifest.files.map((f) => f.path));
10819
- for (const file of localManifest.files) {
10820
- if (!remoteFiles.has(file.path)) {
10821
- if (!categories || categories.includes(file.category)) {
10822
- try {
10823
- if (!dryRun) {
10824
- fs25.unlinkSync(path24.join(vaultPath, file.path));
10825
- }
10826
- result.deleted.push(file.path);
10827
- } catch (err) {
10828
- result.errors.push(`Failed to delete ${file.path}: ${err}`);
10829
- }
10830
- }
10831
- }
10832
- }
10833
- }
10834
- } catch (err) {
10835
- result.errors.push(`Sync failed: ${err}`);
10836
- }
10837
- result.stats.duration = Date.now() - startTime;
10838
- return result;
10839
- }
10840
- function configureTailscaleServe(localPort, options = {}) {
10841
- if (!hasTailscale()) {
10842
- return null;
10843
- }
10844
- const args = ["serve"];
10845
- if (options.funnel) {
10846
- args.push("--bg");
10847
- args.push("funnel");
10848
- } else if (options.background) {
10849
- args.push("--bg");
10850
- }
10851
- args.push(`localhost:${localPort}`);
10852
- const proc = (0, import_child_process4.spawn)("tailscale", args, {
10853
- stdio: "inherit",
10854
- detached: options.background
10855
- });
10856
- if (options.background) {
10857
- proc.unref();
10858
- }
10859
- return proc;
10860
- }
10861
- function stopTailscaleServe() {
10862
- if (!hasTailscale()) {
10863
- return false;
10864
- }
10865
- const result = (0, import_child_process4.spawnSync)("tailscale", ["serve", "off"], {
10866
- stdio: "pipe",
10867
- encoding: "utf-8",
10868
- timeout: 5e3
10869
- });
10870
- return result.status === 0;
10871
- }
10872
- async function checkPeerClawVault(host, port = DEFAULT_SERVE_PORT) {
10873
- try {
10874
- const response = await new Promise((resolve30) => {
10875
- const req = http.get(
10876
- `http://${host}:${port}${CLAWVAULT_SERVE_PATH}/health`,
10877
- { timeout: 5e3 },
10878
- (res) => {
10879
- resolve30(res.statusCode === 200);
10880
- }
10881
- );
10882
- req.on("error", () => resolve30(false));
10883
- req.on("timeout", () => {
10884
- req.destroy();
10885
- resolve30(false);
10886
- });
10887
- });
10888
- return response;
10889
- } catch {
10890
- return false;
10891
- }
10892
- }
10893
- async function discoverClawVaultPeers(port = DEFAULT_SERVE_PORT) {
10894
- const status = getTailscaleStatus();
10895
- if (!status.connected) {
10896
- return [];
10897
- }
10898
- const clawvaultPeers = [];
10899
- const checkPromises = status.peers.filter((p) => p.online).map(async (peer) => {
10900
- const ip = peer.tailscaleIPs[0];
10901
- if (!ip) return;
10902
- const isServing = await checkPeerClawVault(ip, port);
10903
- if (isServing) {
10904
- peer.clawvaultServing = true;
10905
- peer.clawvaultPort = port;
10906
- clawvaultPeers.push(peer);
10907
- }
10908
- });
10909
- await Promise.all(checkPromises);
10910
- return clawvaultPeers;
10911
- }
10912
-
10913
- // src/commands/tailscale.ts
10914
- async function tailscaleStatusCommand(options = {}) {
10915
- const status = getTailscaleStatus();
10916
- if (options.json) {
10917
- console.log(JSON.stringify(status, null, 2));
10918
- return status;
10919
- }
10920
- if (!status.installed) {
10921
- console.log("Tailscale: Not installed");
10922
- console.log(" Install from: https://tailscale.com/download");
10923
- return status;
10924
- }
10925
- const version = getTailscaleVersion();
10926
- console.log(`Tailscale: ${version || "installed"}`);
10927
- if (!status.running) {
10928
- console.log(" Status: Daemon not running");
10929
- if (status.error) {
10930
- console.log(` Error: ${status.error}`);
10931
- }
10932
- return status;
10933
- }
10934
- console.log(` Status: ${status.backendState}`);
10935
- if (status.connected) {
10936
- console.log(` Tailnet: ${status.tailnetName || "unknown"}`);
10937
- console.log(` Self IP: ${status.selfIP || "unknown"}`);
10938
- console.log(` Hostname: ${status.selfHostname || "unknown"}`);
10939
- if (status.selfDNSName) {
10940
- console.log(` DNS Name: ${status.selfDNSName}`);
10941
- }
10942
- if (options.peers || status.peers.length > 0) {
10943
- const onlinePeers = status.peers.filter((p) => p.online);
10944
- const offlinePeers = status.peers.filter((p) => !p.online);
10945
- console.log(`
10946
- Peers (${onlinePeers.length} online, ${offlinePeers.length} offline):`);
10947
- for (const peer of onlinePeers) {
10948
- const ip = peer.tailscaleIPs[0] || "no-ip";
10949
- const os4 = peer.os ? ` (${peer.os})` : "";
10950
- const clawvault = peer.clawvaultServing ? " [ClawVault]" : "";
10951
- console.log(` \u25CF ${peer.hostname}${os4} - ${ip}${clawvault}`);
10952
- }
10953
- if (options.peers) {
10954
- for (const peer of offlinePeers) {
10955
- const ip = peer.tailscaleIPs[0] || "no-ip";
10956
- const os4 = peer.os ? ` (${peer.os})` : "";
10957
- console.log(` \u25CB ${peer.hostname}${os4} - ${ip} [offline]`);
10958
- }
10959
- }
10960
- }
10961
- } else {
10962
- console.log(" Status: Not connected to tailnet");
10963
- if (status.error) {
10964
- console.log(` Error: ${status.error}`);
10965
- }
10966
- }
10967
- return status;
10968
- }
10969
- function registerTailscaleStatusCommand(program) {
10970
- program.command("tailscale-status").alias("ts-status").description("Show Tailscale connection status and peers").option("--json", "Output as JSON").option("--peers", "Show all peers including offline").action(async (rawOptions) => {
10971
- await tailscaleStatusCommand({
10972
- json: rawOptions.json,
10973
- peers: rawOptions.peers
10974
- });
10975
- });
10976
- }
10977
- async function tailscaleSyncCommand(options) {
10978
- const vaultPath = resolveVaultPath({ explicitPath: options.vaultPath });
10979
- const status = getTailscaleStatus();
10980
- if (!status.installed) {
10981
- const error = {
10982
- pushed: [],
10983
- pulled: [],
10984
- deleted: [],
10985
- unchanged: [],
10986
- errors: ["Tailscale not installed. Install from https://tailscale.com/download"],
10987
- stats: { bytesTransferred: 0, filesProcessed: 0, duration: 0 }
10988
- };
10989
- if (options.json) {
10990
- console.log(JSON.stringify(error, null, 2));
10991
- } else {
10992
- console.error("Error: Tailscale not installed");
10993
- }
10994
- return error;
10995
- }
10996
- if (!status.connected) {
10997
- const error = {
10998
- pushed: [],
10999
- pulled: [],
11000
- deleted: [],
11001
- unchanged: [],
11002
- errors: ["Not connected to Tailscale. Run `tailscale up` to connect."],
11003
- stats: { bytesTransferred: 0, filesProcessed: 0, duration: 0 }
11004
- };
11005
- if (options.json) {
11006
- console.log(JSON.stringify(error, null, 2));
11007
- } else {
11008
- console.error("Error: Not connected to Tailscale");
11009
- }
11010
- return error;
11011
- }
11012
- const peer = findPeer(options.peer);
11013
- if (!peer) {
11014
- const error = {
11015
- pushed: [],
11016
- pulled: [],
11017
- deleted: [],
11018
- unchanged: [],
11019
- errors: [`Peer not found: ${options.peer}`],
11020
- stats: { bytesTransferred: 0, filesProcessed: 0, duration: 0 }
11021
- };
11022
- if (options.json) {
11023
- console.log(JSON.stringify(error, null, 2));
11024
- } else {
11025
- console.error(`Error: Peer not found: ${options.peer}`);
11026
- console.log("\nAvailable online peers:");
11027
- for (const p of getOnlinePeers()) {
11028
- console.log(` - ${p.hostname} (${p.tailscaleIPs[0]})`);
11029
- }
11030
- }
11031
- return error;
11032
- }
11033
- if (!peer.online) {
11034
- const error = {
11035
- pushed: [],
11036
- pulled: [],
11037
- deleted: [],
11038
- unchanged: [],
11039
- errors: [`Peer is offline: ${peer.hostname}`],
11040
- stats: { bytesTransferred: 0, filesProcessed: 0, duration: 0 }
11041
- };
11042
- if (options.json) {
11043
- console.log(JSON.stringify(error, null, 2));
11044
- } else {
11045
- console.error(`Error: Peer is offline: ${peer.hostname}`);
11046
- }
11047
- return error;
11048
- }
11049
- const syncOptions = {
11050
- peer: peer.tailscaleIPs[0],
11051
- port: options.port || DEFAULT_SERVE_PORT,
11052
- direction: options.direction || "bidirectional",
11053
- dryRun: options.dryRun,
11054
- deleteOrphans: options.deleteOrphans,
11055
- categories: options.categories,
11056
- https: options.https
11057
- };
11058
- if (!options.json && !options.dryRun) {
11059
- console.log(`Syncing with ${peer.hostname} (${peer.tailscaleIPs[0]})...`);
11060
- }
11061
- const result = await syncWithPeer(vaultPath, syncOptions);
11062
- if (options.json) {
11063
- console.log(JSON.stringify(result, null, 2));
11064
- } else {
11065
- const prefix = options.dryRun ? "[dry-run] " : "";
11066
- if (result.pushed.length > 0) {
11067
- console.log(`
11068
- ${prefix}Pushed ${result.pushed.length} file(s):`);
11069
- for (const file of result.pushed.slice(0, 10)) {
11070
- console.log(` \u2192 ${file}`);
11071
- }
11072
- if (result.pushed.length > 10) {
11073
- console.log(` ... and ${result.pushed.length - 10} more`);
11074
- }
11075
- }
11076
- if (result.pulled.length > 0) {
11077
- console.log(`
11078
- ${prefix}Pulled ${result.pulled.length} file(s):`);
11079
- for (const file of result.pulled.slice(0, 10)) {
11080
- console.log(` \u2190 ${file}`);
11081
- }
11082
- if (result.pulled.length > 10) {
11083
- console.log(` ... and ${result.pulled.length - 10} more`);
11084
- }
11085
- }
11086
- if (result.deleted.length > 0) {
11087
- console.log(`
11088
- ${prefix}Deleted ${result.deleted.length} file(s):`);
11089
- for (const file of result.deleted.slice(0, 10)) {
11090
- console.log(` \u2717 ${file}`);
11091
- }
11092
- if (result.deleted.length > 10) {
11093
- console.log(` ... and ${result.deleted.length - 10} more`);
11094
- }
11095
- }
11096
- if (result.errors.length > 0) {
11097
- console.log(`
11098
- Errors (${result.errors.length}):`);
11099
- for (const error of result.errors) {
11100
- console.log(` ! ${error}`);
11101
- }
11102
- }
11103
- console.log(`
11104
- Summary:`);
11105
- console.log(` Pushed: ${result.pushed.length}`);
11106
- console.log(` Pulled: ${result.pulled.length}`);
11107
- console.log(` Deleted: ${result.deleted.length}`);
11108
- console.log(` Unchanged: ${result.unchanged.length}`);
11109
- console.log(` Errors: ${result.errors.length}`);
11110
- console.log(` Duration: ${result.stats.duration}ms`);
11111
- console.log(` Transferred: ${formatBytes2(result.stats.bytesTransferred)}`);
11112
- }
11113
- return result;
11114
- }
11115
- function formatBytes2(bytes) {
11116
- if (bytes === 0) return "0 B";
11117
- const k = 1024;
11118
- const sizes = ["B", "KB", "MB", "GB"];
11119
- const i = Math.floor(Math.log(bytes) / Math.log(k));
11120
- return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
11121
- }
11122
- function registerTailscaleSyncCommand(program) {
11123
- program.command("tailscale-sync").alias("ts-sync").description("Sync vault with a peer on the Tailscale network").requiredOption("--peer <hostname>", "Peer hostname or IP to sync with").option("-v, --vault <path>", "Vault path").option("--port <number>", "Port on the peer", parseInt).option("--direction <dir>", "Sync direction: push, pull, or bidirectional", "bidirectional").option("--dry-run", "Show what would be synced without making changes").option("--delete-orphans", "Delete files that exist locally but not on peer (pull only)").option("--categories <list>", "Comma-separated list of categories to sync").option("--https", "Use HTTPS for connection").option("--json", "Output as JSON").action(async (rawOptions) => {
11124
- await tailscaleSyncCommand({
11125
- peer: rawOptions.peer,
11126
- vaultPath: rawOptions.vault,
11127
- port: rawOptions.port,
11128
- direction: rawOptions.direction,
11129
- dryRun: rawOptions.dryRun,
11130
- deleteOrphans: rawOptions.deleteOrphans,
11131
- categories: rawOptions.categories?.split(",").map((c) => c.trim()),
11132
- https: rawOptions.https,
11133
- json: rawOptions.json
11134
- });
11135
- });
11136
- }
11137
- var activeServeInstance = null;
11138
- async function tailscaleServeCommand(options) {
11139
- if (options.stop) {
11140
- if (activeServeInstance) {
11141
- await activeServeInstance.stop();
11142
- activeServeInstance = null;
11143
- console.log("ClawVault serve stopped.");
11144
- }
11145
- stopTailscaleServe();
11146
- return;
11147
- }
11148
- const vaultPath = resolveVaultPath({ explicitPath: options.vaultPath });
11149
- const port = options.port || DEFAULT_SERVE_PORT;
11150
- const status = getTailscaleStatus();
11151
- console.log(`Starting ClawVault serve...`);
11152
- console.log(` Vault: ${path25.basename(vaultPath)}`);
11153
- console.log(` Port: ${port}`);
11154
- activeServeInstance = serveVault(vaultPath, { port });
11155
- console.log(` Local URL: http://localhost:${port}/.clawvault`);
11156
- if (status.connected) {
11157
- console.log(` Tailscale URL: http://${status.selfIP}:${port}/.clawvault`);
11158
- if (status.selfDNSName) {
11159
- const dnsHost = status.selfDNSName.replace(/\.$/, "");
11160
- console.log(` MagicDNS URL: http://${dnsHost}:${port}/.clawvault`);
11161
- }
11162
- if (options.funnel || options.background) {
11163
- console.log("\nConfiguring Tailscale serve...");
11164
- configureTailscaleServe(port, {
11165
- funnel: options.funnel,
11166
- background: options.background
11167
- });
11168
- if (options.funnel) {
11169
- console.log(" Funnel enabled - vault is accessible from the public internet");
11170
- }
11171
- }
11172
- } else {
11173
- console.log("\n Note: Not connected to Tailscale. Only local access available.");
11174
- }
11175
- console.log("\nEndpoints:");
11176
- console.log(` Health: /.clawvault/health`);
11177
- console.log(` Manifest: /.clawvault/manifest`);
11178
- console.log(` Files: /.clawvault/files/<path>`);
11179
- if (!options.background) {
11180
- console.log("\nPress Ctrl+C to stop serving.");
11181
- process.on("SIGINT", async () => {
11182
- console.log("\nStopping ClawVault serve...");
11183
- if (activeServeInstance) {
11184
- await activeServeInstance.stop();
11185
- activeServeInstance = null;
11186
- }
11187
- stopTailscaleServe();
11188
- process.exit(0);
11189
- });
11190
- await new Promise(() => {
11191
- });
11192
- }
11193
- }
11194
- function registerTailscaleServeCommand(program) {
11195
- program.command("tailscale-serve").alias("ts-serve").description("Serve vault for sync over Tailscale").option("-v, --vault <path>", "Vault path").option("--port <number>", `Port to serve on (default: ${DEFAULT_SERVE_PORT})`, parseInt).option("--funnel", "Expose via Tailscale Funnel (public internet)").option("--background", "Run in background").option("--stop", "Stop serving").action(async (rawOptions) => {
11196
- await tailscaleServeCommand({
11197
- vaultPath: rawOptions.vault,
11198
- port: rawOptions.port,
11199
- funnel: rawOptions.funnel,
11200
- background: rawOptions.background,
11201
- stop: rawOptions.stop
11202
- });
11203
- });
11204
- }
11205
- async function tailscaleDiscoverCommand(options = {}) {
11206
- const port = options.port || DEFAULT_SERVE_PORT;
11207
- const status = getTailscaleStatus();
11208
- if (!status.connected) {
11209
- if (options.json) {
11210
- console.log(JSON.stringify({ error: "Not connected to Tailscale", peers: [] }));
11211
- } else {
11212
- console.error("Error: Not connected to Tailscale");
11213
- }
11214
- return [];
11215
- }
11216
- if (!options.json) {
11217
- console.log("Discovering ClawVault peers on tailnet...");
11218
- }
11219
- const peers = await discoverClawVaultPeers(port);
11220
- if (options.json) {
11221
- console.log(JSON.stringify({ peers }, null, 2));
11222
- } else {
11223
- if (peers.length === 0) {
11224
- console.log("\nNo ClawVault peers found.");
11225
- console.log(" Run `clawvault tailscale-serve` on other devices to enable sync.");
11226
- } else {
11227
- console.log(`
11228
- Found ${peers.length} ClawVault peer(s):`);
11229
- for (const peer of peers) {
11230
- const ip = peer.tailscaleIPs[0] || "no-ip";
11231
- const os4 = peer.os ? ` (${peer.os})` : "";
11232
- console.log(` \u25CF ${peer.hostname}${os4}`);
11233
- console.log(` IP: ${ip}`);
11234
- console.log(` Port: ${peer.clawvaultPort}`);
11235
- if (peer.dnsName) {
11236
- console.log(` DNS: ${peer.dnsName.replace(/\.$/, "")}`);
11237
- }
11238
- }
11239
- }
11240
- }
11241
- return peers;
11242
- }
11243
- function registerTailscaleDiscoverCommand(program) {
11244
- program.command("tailscale-discover").alias("ts-discover").description("Discover ClawVault peers on the Tailscale network").option("--port <number>", `Port to check (default: ${DEFAULT_SERVE_PORT})`, parseInt).option("--json", "Output as JSON").action(async (rawOptions) => {
11245
- await tailscaleDiscoverCommand({
11246
- port: rawOptions.port,
11247
- json: rawOptions.json
11248
- });
11249
- });
11250
- }
11251
- function registerTailscaleCommands(program) {
11252
- registerTailscaleStatusCommand(program);
11253
- registerTailscaleSyncCommand(program);
11254
- registerTailscaleServeCommand(program);
11255
- registerTailscaleDiscoverCommand(program);
11256
- }
11257
-
11258
- // src/cli/index.ts
11259
- function registerCliCommands(program) {
11260
- registerContextCommand(program);
11261
- registerInjectCommand(program);
11262
- registerObserveCommand(program);
11263
- registerReflectCommand(program);
11264
- registerEmbedCommand(program);
11265
- registerReweaveCommand(program);
11266
- registerTailscaleCommands(program);
11267
- return program;
11268
- }
11269
-
11270
- // src/commands/setup.ts
11271
- var fs26 = __toESM(require("fs"), 1);
11272
- var os2 = __toESM(require("os"), 1);
11273
- var path26 = __toESM(require("path"), 1);
11274
- var import_child_process5 = require("child_process");
11275
- var import_gray_matter7 = __toESM(require("gray-matter"), 1);
11276
- var CONFIG_FILE4 = ".clawvault.json";
11277
- function resolveVaultTarget(vaultOverride) {
11278
- if (vaultOverride) {
11279
- const vaultPath = path26.resolve(vaultOverride);
11280
- return { vaultPath, source: "--vault flag", existed: fs26.existsSync(vaultPath) };
11281
- }
11282
- const envPath = process.env.CLAWVAULT_PATH?.trim();
11283
- const home = os2.homedir();
11284
- if (envPath) {
11285
- const vaultPath = path26.resolve(envPath);
11286
- return { vaultPath, source: "CLAWVAULT_PATH", existed: fs26.existsSync(vaultPath) };
11287
- }
11288
- const candidates = [
11289
- { vaultPath: path26.join(home, ".openclaw", "workspace", "memory"), source: "OpenClaw default" },
11290
- { vaultPath: path26.resolve(process.cwd(), "memory"), source: "./memory" },
11291
- { vaultPath: path26.join(home, "memory"), source: "~/memory" }
11292
- ];
11293
- for (const candidate of candidates) {
11294
- if (fs26.existsSync(candidate.vaultPath)) {
11295
- return { ...candidate, existed: true };
11296
- }
11297
- }
11298
- const fallback = candidates[0];
11299
- return { ...fallback, existed: false };
11300
- }
11301
- function ensureVaultStructure(vaultPath) {
11302
- fs26.mkdirSync(vaultPath, { recursive: true });
11303
- for (const category of DEFAULT_CATEGORIES) {
11304
- fs26.mkdirSync(path26.join(vaultPath, category), { recursive: true });
11305
- }
11306
- const configPath = path26.join(vaultPath, CONFIG_FILE4);
11307
- if (fs26.existsSync(configPath)) return false;
11308
- const now = (/* @__PURE__ */ new Date()).toISOString();
11309
- const name = path26.basename(vaultPath);
11310
- const meta = {
11311
- name,
11312
- version: "1.0.0",
11313
- created: now,
11314
- lastUpdated: now,
11315
- categories: DEFAULT_CATEGORIES,
11316
- documentCount: 0,
11317
- qmdCollection: name,
11318
- qmdRoot: vaultPath
11319
- };
11320
- fs26.writeFileSync(configPath, JSON.stringify(meta, null, 2));
11321
- return true;
11322
- }
11323
- function writeBases(vaultPath, force) {
11324
- const basesFiles = {
11325
- "all-tasks.base": `filters:
11326
- and:
11327
- - file.inFolder("tasks")
11328
- - status != "done"
11329
- formulas:
11330
- age: (now() - file.ctime).days
11331
- status_icon: if(status == "blocked", "\u{1F534}", if(status == "in-progress", "\u{1F528}", if(status == "open", "\u26AA", "\u2705")))
11332
- views:
11333
- - type: table
11334
- name: All Active Tasks
11335
- groupBy:
11336
- property: status
11337
- direction: ASC
11338
- order:
11339
- - formula.status_icon
11340
- - file.name
11341
- - status
11342
- - owner
11343
- - project
11344
- - priority
11345
- - blocked_by
11346
- - formula.age
11347
- - type: cards
11348
- name: Task Board
11349
- groupBy:
11350
- property: status
11351
- direction: ASC
11352
- order:
11353
- - file.name
11354
- - owner
11355
- - project
11356
- - priority`,
11357
- "blocked.base": `filters:
11358
- and:
11359
- - file.inFolder("tasks")
11360
- - status == "blocked"
11361
- formulas:
11362
- days_blocked: (now() - file.ctime).days
11363
- views:
11364
- - type: table
11365
- name: Blocked Tasks
11366
- order:
11367
- - file.name
11368
- - owner
11369
- - project
11370
- - blocked_by
11371
- - formula.days_blocked
11372
- - priority`,
11373
- "by-project.base": `filters:
11374
- and:
11375
- - file.inFolder("tasks")
11376
- - status != "done"
11377
- formulas:
11378
- status_icon: if(status == "blocked", "\u{1F534}", if(status == "in-progress", "\u{1F528}", "\u26AA"))
11379
- views:
11380
- - type: table
11381
- name: By Project
11382
- groupBy:
11383
- property: project
11384
- direction: ASC
11385
- order:
11386
- - formula.status_icon
11387
- - file.name
11388
- - status
11389
- - owner
11390
- - priority
11391
- - type: cards
11392
- name: Project Cards
11393
- groupBy:
11394
- property: project
11395
- direction: ASC
11396
- order:
11397
- - file.name
11398
- - owner
11399
- - status`,
11400
- "by-owner.base": `filters:
11401
- and:
11402
- - file.inFolder("tasks")
11403
- - status != "done"
11404
- views:
11405
- - type: table
11406
- name: By Owner
11407
- groupBy:
11408
- property: owner
11409
- direction: ASC
11410
- order:
11411
- - file.name
11412
- - status
11413
- - project
11414
- - priority`,
11415
- "backlog.base": `filters:
11416
- and:
11417
- - file.inFolder("backlog")
11418
- views:
11419
- - type: table
11420
- name: Backlog
11421
- order:
11422
- - file.name
11423
- - source
11424
- - project
11425
- - file.ctime`
11426
- };
11427
- let written = 0;
11428
- for (const [filename, content] of Object.entries(basesFiles)) {
11429
- const filePath = path26.join(vaultPath, filename);
11430
- if (force || !fs26.existsSync(filePath)) {
11431
- fs26.writeFileSync(filePath, content);
11432
- written++;
11433
- }
11434
- }
11435
- return written;
11436
- }
11437
- var NEURAL_GRAPH_CSS = `/* ClawVault Graph Colors \u2014 Neural Neural Style */
11438
- /* Auto-generated by \`clawvault setup --theme neural\` */
11439
-
11440
- body.theme-dark .graph-view .graph-view-container { background-color: #0a0a0a; }
11441
-
11442
- body.theme-dark .graph-view .node.tag-person circle { fill: #00b4d8 !important; }
11443
- body.theme-dark .graph-view .node.tag-project circle { fill: #2d6a4f !important; }
11444
- body.theme-dark .graph-view .node.tag-decision circle { fill: #e8590c !important; }
11445
- body.theme-dark .graph-view .node.tag-lesson circle { fill: #fcc419 !important; }
11446
- body.theme-dark .graph-view .node.tag-commitment circle { fill: #e03131 !important; }
11447
- body.theme-dark .graph-view .node.tag-task circle { fill: #22b8cf !important; }
11448
- body.theme-dark .graph-view .node.tag-observation circle { fill: #7950f2 !important; }
11449
- body.theme-dark .graph-view .node.tag-handoff circle { fill: #845ef7 !important; }
11450
- body.theme-dark .graph-view .node.tag-daily circle { fill: #495057 !important; }
11451
-
11452
- body.theme-dark .graph-view .node.is-focused circle {
11453
- fill: #e8a430 !important; stroke: #e8a430 !important;
11454
- stroke-width: 3px; filter: drop-shadow(0 0 6px #e8a430);
11455
- }
11456
-
11457
- body.theme-dark .graph-view .link { stroke: rgba(45, 200, 120, 0.15) !important; }
11458
- body.theme-dark .graph-view .link.is-focused { stroke: rgba(45, 200, 120, 0.6) !important; }
11459
- body.theme-dark .graph-view .node text { fill: #c1c2c5 !important; font-size: 0.8em; }
11460
- `;
11461
- var MINIMAL_GRAPH_CSS = `/* ClawVault Graph Colors \u2014 Minimal */
11462
- /* Auto-generated by \`clawvault setup --theme minimal\` */
11463
-
11464
- body.theme-dark .graph-view .node.tag-person circle { fill: #4a90e8 !important; }
11465
- body.theme-dark .graph-view .node.tag-project circle { fill: #4ae85d !important; }
11466
- body.theme-dark .graph-view .node.tag-decision circle { fill: #e85d4a !important; }
11467
- body.theme-dark .graph-view .node.tag-lesson circle { fill: #9b59b6 !important; }
11468
- body.theme-dark .graph-view .node.tag-task circle { fill: #e8a430 !important; }
11469
- `;
11470
- var NEURAL_COLOR_GROUPS = [
11471
- { query: "path:people", color: { a: 1, rgb: 47316 } },
11472
- { query: "path:projects", color: { a: 1, rgb: 2976335 } },
11473
- { query: "path:decisions", color: { a: 1, rgb: 15227916 } },
11474
- { query: "path:lessons", color: { a: 1, rgb: 16565273 } },
11475
- { query: "path:tasks", color: { a: 1, rgb: 2275535 } },
11476
- { query: "path:commitments", color: { a: 1, rgb: 14680369 } },
11477
- { query: "path:backlog", color: { a: 1, rgb: 9806262 } },
11478
- { query: "path:inbox", color: { a: 1, rgb: 15964178 } },
11479
- { query: "path:handoffs", color: { a: 1, rgb: 8675063 } },
11480
- { query: "path:ledger", color: { a: 1, rgb: 7950066 } }
11481
- ];
11482
- var MINIMAL_COLOR_GROUPS = [
11483
- { query: "path:people", color: { a: 1, rgb: 4886760 } },
11484
- { query: "path:projects", color: { a: 1, rgb: 4909149 } },
11485
- { query: "path:decisions", color: { a: 1, rgb: 15228234 } },
11486
- { query: "path:lessons", color: { a: 1, rgb: 10181046 } },
11487
- { query: "path:tasks", color: { a: 1, rgb: 15246384 } }
11488
- ];
11489
- function writeGraphColors(vaultPath, theme, force) {
11490
- const obsidianDir = path26.join(vaultPath, ".obsidian");
11491
- if (!fs26.existsSync(obsidianDir)) {
11492
- return false;
11493
- }
11494
- const snippetsDir = path26.join(obsidianDir, "snippets");
11495
- fs26.mkdirSync(snippetsDir, { recursive: true });
11496
- const snippetName = "clawvault-graph";
11497
- const snippetPath = path26.join(snippetsDir, `${snippetName}.css`);
11498
- if (!force && fs26.existsSync(snippetPath)) {
11499
- return false;
11500
- }
11501
- const css = theme === "neural" ? NEURAL_GRAPH_CSS : MINIMAL_GRAPH_CSS;
11502
- fs26.writeFileSync(snippetPath, css);
11503
- const appearancePath = path26.join(obsidianDir, "appearance.json");
11504
- let appearance = {};
11505
- if (fs26.existsSync(appearancePath)) {
11506
- try {
11507
- appearance = JSON.parse(fs26.readFileSync(appearancePath, "utf-8"));
11508
- } catch {
11509
- }
11510
- }
11511
- const snippets = appearance.enabledCssSnippets || [];
11512
- if (!snippets.includes(snippetName)) {
11513
- snippets.push(snippetName);
11514
- appearance.enabledCssSnippets = snippets;
11515
- fs26.writeFileSync(appearancePath, JSON.stringify(appearance, null, 2));
11516
- }
11517
- const graphPath = path26.join(obsidianDir, "graph.json");
11518
- let graphConfig = {};
11519
- if (fs26.existsSync(graphPath)) {
11520
- try {
11521
- graphConfig = JSON.parse(fs26.readFileSync(graphPath, "utf-8"));
11522
- } catch {
11523
- }
11524
- }
11525
- graphConfig.colorGroups = theme === "neural" ? NEURAL_COLOR_GROUPS : MINIMAL_COLOR_GROUPS;
11526
- if (theme === "neural") {
11527
- graphConfig.showTags = false;
11528
- graphConfig.showAttachments = false;
11529
- graphConfig.nodeSizeMultiplier = 1.2;
11530
- graphConfig.lineSizeMultiplier = 0.8;
11531
- graphConfig.textFadeMultiplier = 0;
11532
- graphConfig.repelStrength = 10;
11533
- graphConfig.linkDistance = 250;
11534
- graphConfig.centerStrength = 0.5;
11535
- }
11536
- fs26.writeFileSync(graphPath, JSON.stringify(graphConfig, null, 2));
11537
- return true;
11538
- }
11539
- function getQmdConfig(vaultPath) {
11540
- const configPath = path26.join(vaultPath, CONFIG_FILE4);
11541
- if (fs26.existsSync(configPath)) {
11542
- try {
11543
- const meta = JSON.parse(fs26.readFileSync(configPath, "utf-8"));
11544
- return {
11545
- collection: meta.qmdCollection || meta.name || path26.basename(vaultPath),
11546
- root: meta.qmdRoot || vaultPath
11547
- };
11548
- } catch {
11549
- return { collection: path26.basename(vaultPath), root: vaultPath };
11550
- }
11551
- }
11552
- return { collection: path26.basename(vaultPath), root: vaultPath };
11553
- }
11554
- function slugify2(text) {
11555
- return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").trim();
11556
- }
11557
- function scanMarkdownFiles(dirPath) {
11558
- const files = [];
11559
- const resolvedPath = path26.resolve(dirPath);
11560
- if (!fs26.existsSync(resolvedPath)) {
11561
- return files;
11562
- }
11563
- const stat = fs26.statSync(resolvedPath);
11564
- if (stat.isFile() && resolvedPath.endsWith(".md")) {
11565
- return [resolvedPath];
11566
- }
11567
- if (!stat.isDirectory()) {
11568
- return files;
11569
- }
11570
- const entries = fs26.readdirSync(resolvedPath, { withFileTypes: true });
11571
- for (const entry of entries) {
11572
- const fullPath = path26.join(resolvedPath, entry.name);
11573
- if (entry.isFile() && entry.name.endsWith(".md")) {
11574
- files.push(fullPath);
11575
- } else if (entry.isDirectory()) {
11576
- files.push(...scanMarkdownFiles(fullPath));
11577
- }
11578
- }
11579
- return files;
11580
- }
11581
- function extractPeople(content) {
11582
- const people = [];
11583
- const seenNames = /* @__PURE__ */ new Set();
11584
- const emailPattern = /([A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)\s*<([^>]+@[^>]+)>/g;
11585
- let match;
11586
- while ((match = emailPattern.exec(content)) !== null) {
11587
- const name = match[1].trim();
11588
- const email = match[2].trim();
11589
- if (!seenNames.has(name.toLowerCase())) {
11590
- seenNames.add(name.toLowerCase());
11591
- people.push({ name, email });
11592
- }
11593
- }
11594
- const rolePatterns = [
11595
- /([A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)\s*\(([^)]+)\)/g,
11596
- /([A-Z][a-z]+(?:\s+[A-Z][a-z]+)+),\s*(CEO|CTO|CFO|COO|VP|Director|Manager|Engineer|Designer|Lead|Head of [A-Za-z]+)/gi
11597
- ];
11598
- for (const pattern of rolePatterns) {
11599
- while ((match = pattern.exec(content)) !== null) {
11600
- const name = match[1].trim();
11601
- const role = match[2].trim();
11602
- if (!seenNames.has(name.toLowerCase())) {
11603
- seenNames.add(name.toLowerCase());
11604
- people.push({ name, role });
11605
- }
11606
- }
11607
- }
11608
- const contextPatterns = [
11609
- /(?:contact|met with|spoke with|emailed|called|messaged|talked to|meeting with)\s+([A-Z][a-zA-Z]*(?:\s+[A-Z][a-zA-Z]*)+)/g,
11610
- /([A-Z][a-zA-Z]*(?:\s+[A-Z][a-zA-Z]*)+)\s+(?:said|mentioned|suggested|recommended|asked|told me)/g
11611
- ];
11612
- for (const pattern of contextPatterns) {
11613
- while ((match = pattern.exec(content)) !== null) {
11614
- const name = match[1].trim();
11615
- if (!seenNames.has(name.toLowerCase()) && name.split(" ").length >= 2) {
11616
- seenNames.add(name.toLowerCase());
11617
- const lineStart = content.lastIndexOf("\n", match.index) + 1;
11618
- const lineEnd = content.indexOf("\n", match.index + match[0].length);
11619
- const context = content.slice(lineStart, lineEnd === -1 ? void 0 : lineEnd).trim();
11620
- people.push({ name, context: context.slice(0, 200) });
11621
- }
11622
- }
11623
- }
11624
- return people;
11625
- }
11626
- function extractPreferences2(content) {
11627
- const preferences = [];
11628
- const seenPrefs = /* @__PURE__ */ new Set();
11629
- const patterns = [
11630
- // "prefers X" / "prefer X" / "I prefer X"
11631
- /(?:I\s+)?prefer(?:s)?\s+(?:to\s+)?([^.,\n]+)/gi,
11632
- // "likes X" / "like X"
11633
- /(?:I\s+)?like(?:s)?\s+(?:to\s+)?([^.,\n]+)/gi,
11634
- // "always use X"
11635
- /always\s+(?:use|uses?)\s+([^.,\n]+)/gi,
11636
- // "never use X"
11637
- /never\s+(?:use|uses?)\s+([^.,\n]+)/gi,
11638
- // "favorite X is Y"
11639
- /(?:my\s+)?favorite\s+(\w+)\s+is\s+([^.,\n]+)/gi,
11640
- // "prefer X over Y"
11641
- /prefer(?:s)?\s+([^.,\n]+)\s+over\s+([^.,\n]+)/gi,
11642
- // "use X instead of Y"
11643
- /use(?:s)?\s+([^.,\n]+)\s+instead\s+of\s+([^.,\n]+)/gi
11644
- ];
11645
- for (const pattern of patterns) {
11646
- let match;
11647
- while ((match = pattern.exec(content)) !== null) {
11648
- const fullMatch = match[0].trim();
11649
- const key = fullMatch.toLowerCase();
11650
- if (!seenPrefs.has(key) && fullMatch.length > 5 && fullMatch.length < 200) {
11651
- seenPrefs.add(key);
11652
- const lineStart = content.lastIndexOf("\n", match.index) + 1;
11653
- const lineEnd = content.indexOf("\n", match.index + match[0].length);
11654
- const context = content.slice(lineStart, lineEnd === -1 ? void 0 : lineEnd).trim();
11655
- let subject = "general";
11656
- let preference = fullMatch;
11657
- if (match[2]) {
11658
- subject = match[1].trim();
11659
- preference = match[2].trim();
11660
- } else if (match[1]) {
11661
- preference = match[1].trim();
11662
- }
11663
- preferences.push({ subject, preference, context: context.slice(0, 200) });
11664
- }
11665
- }
11666
- }
11667
- return preferences;
11668
- }
11669
- function extractDecisions(content) {
11670
- const decisions = [];
11671
- const seenDecisions = /* @__PURE__ */ new Set();
11672
- const patterns = [
11673
- // "decided to X"
11674
- /(?:I\s+|we\s+)?decided\s+(?:to\s+)?([^.,\n]+)/gi,
11675
- // "decision: X" or "Decision - X"
11676
- /decision[:\s-]+([^.,\n]+)/gi,
11677
- // "we chose X" / "chose to X"
11678
- /(?:we\s+)?chose\s+(?:to\s+)?([^.,\n]+)/gi,
11679
- // "going with X"
11680
- /going\s+with\s+([^.,\n]+)/gi,
11681
- // "settled on X"
11682
- /settled\s+on\s+([^.,\n]+)/gi,
11683
- // "will use X" / "using X going forward"
11684
- /(?:will\s+use|using)\s+([^.,\n]+)\s+(?:going\s+forward|from\s+now\s+on)/gi
11685
- ];
11686
- for (const pattern of patterns) {
11687
- let match;
11688
- while ((match = pattern.exec(content)) !== null) {
11689
- const decision = match[1].trim();
11690
- const key = decision.toLowerCase();
11691
- if (!seenDecisions.has(key) && decision.length > 5 && decision.length < 200) {
11692
- seenDecisions.add(key);
11693
- const lineStart = content.lastIndexOf("\n", match.index) + 1;
11694
- const lineEnd = content.indexOf("\n", match.index + match[0].length);
11695
- const context = content.slice(lineStart, lineEnd === -1 ? void 0 : lineEnd).trim();
11696
- const title = decision.length > 50 ? decision.slice(0, 50).trim() + "..." : decision;
11697
- decisions.push({ title, decision, context: context.slice(0, 200) });
11698
- }
10040
+ const graphPath = path23.join(obsidianDir, "graph.json");
10041
+ let graphConfig = {};
10042
+ if (fs24.existsSync(graphPath)) {
10043
+ try {
10044
+ graphConfig = JSON.parse(fs24.readFileSync(graphPath, "utf-8"));
10045
+ } catch {
11699
10046
  }
11700
10047
  }
11701
- return decisions;
11702
- }
11703
- function extractTasks(content) {
11704
- const tasks = [];
11705
- const seenTasks = /* @__PURE__ */ new Set();
11706
- const patterns = [
11707
- // Markdown checkbox "- [ ] task"
11708
- /^[\s]*[-*]\s*\[\s*\]\s*(.+)$/gm,
11709
- // "TODO: task" or "TODO - task"
11710
- /TODO[:\s-]+(.+)/gi,
11711
- // "FIXME: task"
11712
- /FIXME[:\s-]+(.+)/gi,
11713
- // "need to X"
11714
- /(?:I\s+|we\s+)?need\s+to\s+([^.,\n]+)/gi,
11715
- // "should X" (but not "should have" which is past)
11716
- /(?:I\s+|we\s+)?should\s+(?!have)([^.,\n]+)/gi,
11717
- // "must X"
11718
- /(?:I\s+|we\s+)?must\s+([^.,\n]+)/gi,
11719
- // "remember to X"
11720
- /remember\s+to\s+([^.,\n]+)/gi,
11721
- // "don't forget to X"
11722
- /don'?t\s+forget\s+to\s+([^.,\n]+)/gi
11723
- ];
11724
- for (const pattern of patterns) {
11725
- let match;
11726
- while ((match = pattern.exec(content)) !== null) {
11727
- const taskText = match[1].trim();
11728
- const key = taskText.toLowerCase();
11729
- if (!seenTasks.has(key) && taskText.length > 3 && taskText.length < 200) {
11730
- seenTasks.add(key);
11731
- let priority = "medium";
11732
- const lowerTask = taskText.toLowerCase();
11733
- if (lowerTask.includes("urgent") || lowerTask.includes("asap") || lowerTask.includes("critical")) {
11734
- priority = "critical";
11735
- } else if (lowerTask.includes("important") || lowerTask.includes("high priority")) {
11736
- priority = "high";
11737
- } else if (lowerTask.includes("low priority") || lowerTask.includes("eventually") || lowerTask.includes("someday")) {
11738
- priority = "low";
11739
- }
11740
- tasks.push({ title: taskText, priority });
11741
- }
11742
- }
10048
+ graphConfig.colorGroups = theme === "neural" ? NEURAL_COLOR_GROUPS : MINIMAL_COLOR_GROUPS;
10049
+ if (theme === "neural") {
10050
+ graphConfig.showTags = false;
10051
+ graphConfig.showAttachments = false;
10052
+ graphConfig.nodeSizeMultiplier = 1.2;
10053
+ graphConfig.lineSizeMultiplier = 0.8;
10054
+ graphConfig.textFadeMultiplier = 0;
10055
+ graphConfig.repelStrength = 10;
10056
+ graphConfig.linkDistance = 250;
10057
+ graphConfig.centerStrength = 0.5;
11743
10058
  }
11744
- return tasks;
11745
- }
11746
- function extractFromContent(content) {
11747
- return {
11748
- people: extractPeople(content),
11749
- preferences: extractPreferences2(content),
11750
- decisions: extractDecisions(content),
11751
- tasks: extractTasks(content)
11752
- };
10059
+ fs24.writeFileSync(graphPath, JSON.stringify(graphConfig, null, 2));
10060
+ return true;
11753
10061
  }
11754
- function scanAndExtract(sourcePath) {
11755
- const files = scanMarkdownFiles(sourcePath);
11756
- const combined = {
11757
- people: [],
11758
- preferences: [],
11759
- decisions: [],
11760
- tasks: []
11761
- };
11762
- for (const file of files) {
10062
+ function getQmdConfig(vaultPath) {
10063
+ const configPath = path23.join(vaultPath, CONFIG_FILE4);
10064
+ if (fs24.existsSync(configPath)) {
11763
10065
  try {
11764
- const content = fs26.readFileSync(file, "utf-8");
11765
- const extracted = extractFromContent(content);
11766
- combined.people.push(...extracted.people);
11767
- combined.preferences.push(...extracted.preferences);
11768
- combined.decisions.push(...extracted.decisions);
11769
- combined.tasks.push(...extracted.tasks);
10066
+ const meta = JSON.parse(fs24.readFileSync(configPath, "utf-8"));
10067
+ return {
10068
+ collection: meta.qmdCollection || meta.name || path23.basename(vaultPath),
10069
+ root: meta.qmdRoot || vaultPath
10070
+ };
11770
10071
  } catch {
10072
+ return { collection: path23.basename(vaultPath), root: vaultPath };
11771
10073
  }
11772
10074
  }
11773
- const seenPeople = /* @__PURE__ */ new Set();
11774
- combined.people = combined.people.filter((p) => {
11775
- const key = p.name.toLowerCase();
11776
- if (seenPeople.has(key)) return false;
11777
- seenPeople.add(key);
11778
- return true;
11779
- });
11780
- const seenPrefs = /* @__PURE__ */ new Set();
11781
- combined.preferences = combined.preferences.filter((p) => {
11782
- const key = `${p.subject}:${p.preference}`.toLowerCase();
11783
- if (seenPrefs.has(key)) return false;
11784
- seenPrefs.add(key);
11785
- return true;
11786
- });
11787
- const seenDecisions = /* @__PURE__ */ new Set();
11788
- combined.decisions = combined.decisions.filter((d) => {
11789
- const key = d.decision.toLowerCase();
11790
- if (seenDecisions.has(key)) return false;
11791
- seenDecisions.add(key);
11792
- return true;
11793
- });
11794
- const seenTasks = /* @__PURE__ */ new Set();
11795
- combined.tasks = combined.tasks.filter((t) => {
11796
- const key = t.title.toLowerCase();
11797
- if (seenTasks.has(key)) return false;
11798
- seenTasks.add(key);
11799
- return true;
11800
- });
11801
- return combined;
11802
- }
11803
- function fileExistsWithSimilarName(dir, slug) {
11804
- if (!fs26.existsSync(dir)) return false;
11805
- const files = fs26.readdirSync(dir);
11806
- const targetSlug = slug.toLowerCase();
11807
- return files.some((f) => {
11808
- const fileSlug = path26.basename(f, ".md").toLowerCase();
11809
- return fileSlug === targetSlug || fileSlug.includes(targetSlug) || targetSlug.includes(fileSlug);
11810
- });
11811
- }
11812
- function writePerson(vaultPath, person, force) {
11813
- const peopleDir = path26.join(vaultPath, "people");
11814
- fs26.mkdirSync(peopleDir, { recursive: true });
11815
- const slug = slugify2(person.name);
11816
- if (!slug) return null;
11817
- const filePath = path26.join(peopleDir, `${slug}.md`);
11818
- if (!force && (fs26.existsSync(filePath) || fileExistsWithSimilarName(peopleDir, slug))) {
11819
- return null;
11820
- }
11821
- const template = loadTemplateDefinition("person");
11822
- const now = /* @__PURE__ */ new Date();
11823
- const date = now.toISOString().split("T")[0];
11824
- let content;
11825
- if (template) {
11826
- const rendered = renderDocumentFromTemplate(template, {
11827
- title: person.name,
11828
- now,
11829
- overrides: {
11830
- relationship: person.role ? "colleague" : "contact"
11831
- }
11832
- });
11833
- let body = rendered.content;
11834
- if (person.context) {
11835
- body = body.replace(/## Context\n-\s*/, `## Context
11836
- - ${person.context}
11837
- `);
11838
- }
11839
- if (person.email || person.role) {
11840
- const details = [];
11841
- if (person.email) details.push(`- Contact: ${person.email}`);
11842
- if (person.role) details.push(`- Role: ${person.role}`);
11843
- body = body.replace(/## Details\n- Contact:\n- Role:\n- Timezone:/, `## Details
11844
- ${details.join("\n")}
11845
- - Timezone:`);
11846
- }
11847
- content = import_gray_matter7.default.stringify(body, rendered.frontmatter);
11848
- } else {
11849
- const frontmatter = {
11850
- title: person.name,
11851
- date,
11852
- type: "person",
11853
- relationship: person.role ? "colleague" : "contact"
11854
- };
11855
- let body = `# ${person.name}
11856
-
11857
- ## Context
11858
- `;
11859
- if (person.context) body += `- ${person.context}
11860
- `;
11861
- else body += "- \n";
11862
- body += "\n## Details\n";
11863
- if (person.email) body += `- Contact: ${person.email}
11864
- `;
11865
- else body += "- Contact:\n";
11866
- if (person.role) body += `- Role: ${person.role}
11867
- `;
11868
- else body += "- Role:\n";
11869
- body += "- Timezone:\n";
11870
- body += `
11871
- ## History
11872
- - ${date}: Added from memory import
11873
- `;
11874
- content = import_gray_matter7.default.stringify(body, frontmatter);
11875
- }
11876
- fs26.writeFileSync(filePath, content);
11877
- return slug;
11878
- }
11879
- function writePreference(vaultPath, pref, force) {
11880
- const prefsDir = path26.join(vaultPath, "preferences");
11881
- fs26.mkdirSync(prefsDir, { recursive: true });
11882
- const slug = slugify2(`${pref.subject}-${pref.preference}`.slice(0, 60));
11883
- if (!slug) return null;
11884
- const filePath = path26.join(prefsDir, `${slug}.md`);
11885
- if (!force && (fs26.existsSync(filePath) || fileExistsWithSimilarName(prefsDir, slug))) {
11886
- return null;
11887
- }
11888
- const now = /* @__PURE__ */ new Date();
11889
- const date = now.toISOString().split("T")[0];
11890
- const frontmatter = {
11891
- title: `${pref.subject}: ${pref.preference}`.slice(0, 100),
11892
- date,
11893
- type: "preference",
11894
- category: pref.subject
11895
- };
11896
- let body = `# ${frontmatter.title}
11897
-
11898
- `;
11899
- body += `## Preference
11900
- - ${pref.preference}
11901
- `;
11902
- if (pref.context) {
11903
- body += `
11904
- ## Context
11905
- - ${pref.context}
11906
- `;
11907
- }
11908
- body += `
11909
- ## Source
11910
- - Imported from memory on ${date}
11911
- `;
11912
- const content = import_gray_matter7.default.stringify(body, frontmatter);
11913
- fs26.writeFileSync(filePath, content);
11914
- return slug;
11915
- }
11916
- function writeDecision(vaultPath, decision, force) {
11917
- const decisionsDir = path26.join(vaultPath, "decisions");
11918
- fs26.mkdirSync(decisionsDir, { recursive: true });
11919
- const slug = slugify2(decision.title);
11920
- if (!slug) return null;
11921
- const filePath = path26.join(decisionsDir, `${slug}.md`);
11922
- if (!force && (fs26.existsSync(filePath) || fileExistsWithSimilarName(decisionsDir, slug))) {
11923
- return null;
11924
- }
11925
- const template = loadTemplateDefinition("decision");
11926
- const now = /* @__PURE__ */ new Date();
11927
- const date = now.toISOString().split("T")[0];
11928
- let content;
11929
- if (template) {
11930
- const rendered = renderDocumentFromTemplate(template, {
11931
- title: decision.title,
11932
- now,
11933
- overrides: {
11934
- status: "decided"
11935
- }
11936
- });
11937
- let body = rendered.content;
11938
- if (decision.context) {
11939
- body = body.replace(/## Context\n-\s*/, `## Context
11940
- - ${decision.context}
11941
- `);
11942
- }
11943
- body = body.replace(/## Decision\n-\s*/, `## Decision
11944
- - ${decision.decision}
11945
- `);
11946
- body = body.replace(/## Consequences\n-\s*/, `## Consequences
11947
- - Imported from memory on ${date}
11948
- `);
11949
- content = import_gray_matter7.default.stringify(body, rendered.frontmatter);
11950
- } else {
11951
- const frontmatter = {
11952
- title: decision.title,
11953
- date,
11954
- type: "decision",
11955
- status: "decided"
11956
- };
11957
- let body = `# Decision: ${decision.title}
11958
-
11959
- `;
11960
- body += `## Context
11961
- `;
11962
- if (decision.context) body += `- ${decision.context}
11963
- `;
11964
- else body += "- \n";
11965
- body += `
11966
- ## Decision
11967
- - ${decision.decision}
11968
- `;
11969
- body += `
11970
- ## Consequences
11971
- - Imported from memory on ${date}
11972
- `;
11973
- content = import_gray_matter7.default.stringify(body, frontmatter);
11974
- }
11975
- fs26.writeFileSync(filePath, content);
11976
- return slug;
11977
- }
11978
- function writeTask(vaultPath, task, force) {
11979
- const tasksDir = path26.join(vaultPath, "tasks");
11980
- fs26.mkdirSync(tasksDir, { recursive: true });
11981
- const slug = slugify2(task.title);
11982
- if (!slug) return null;
11983
- const filePath = path26.join(tasksDir, `${slug}.md`);
11984
- if (!force && (fs26.existsSync(filePath) || fileExistsWithSimilarName(tasksDir, slug))) {
11985
- return null;
11986
- }
11987
- const template = loadTemplateDefinition("task");
11988
- const now = /* @__PURE__ */ new Date();
11989
- const datetime = now.toISOString();
11990
- let content;
11991
- if (template) {
11992
- const rendered = renderDocumentFromTemplate(template, {
11993
- title: task.title,
11994
- now,
11995
- overrides: {
11996
- status: "open",
11997
- priority: task.priority || "medium",
11998
- source: "memory-import",
11999
- description: task.description
12000
- },
12001
- frontmatter: { pruneEmpty: true }
12002
- });
12003
- content = rendered.markdown;
12004
- } else {
12005
- const frontmatter = {
12006
- status: "open",
12007
- source: "memory-import",
12008
- created: datetime,
12009
- updated: datetime,
12010
- priority: task.priority || "medium"
12011
- };
12012
- if (task.description) frontmatter.description = task.description;
12013
- const body = `# ${task.title}
12014
-
12015
- `;
12016
- content = import_gray_matter7.default.stringify(body, frontmatter);
12017
- }
12018
- fs26.writeFileSync(filePath, content);
12019
- return slug;
12020
- }
12021
- function importToVault(vaultPath, extracted, force) {
12022
- const summary = {
12023
- created: { people: [], preferences: [], decisions: [], tasks: [] },
12024
- skipped: { people: [], preferences: [], decisions: [], tasks: [] }
12025
- };
12026
- for (const person of extracted.people) {
12027
- const slug = writePerson(vaultPath, person, force);
12028
- if (slug) {
12029
- summary.created.people.push(person.name);
12030
- } else {
12031
- summary.skipped.people.push(person.name);
12032
- }
12033
- }
12034
- for (const pref of extracted.preferences) {
12035
- const slug = writePreference(vaultPath, pref, force);
12036
- if (slug) {
12037
- summary.created.preferences.push(`${pref.subject}: ${pref.preference}`.slice(0, 50));
12038
- } else {
12039
- summary.skipped.preferences.push(`${pref.subject}: ${pref.preference}`.slice(0, 50));
12040
- }
12041
- }
12042
- for (const decision of extracted.decisions) {
12043
- const slug = writeDecision(vaultPath, decision, force);
12044
- if (slug) {
12045
- summary.created.decisions.push(decision.title);
12046
- } else {
12047
- summary.skipped.decisions.push(decision.title);
12048
- }
12049
- }
12050
- for (const task of extracted.tasks) {
12051
- const slug = writeTask(vaultPath, task, force);
12052
- if (slug) {
12053
- summary.created.tasks.push(task.title);
12054
- } else {
12055
- summary.skipped.tasks.push(task.title);
12056
- }
12057
- }
12058
- return summary;
12059
- }
12060
- function printImportSummary(summary) {
12061
- const totalCreated = summary.created.people.length + summary.created.preferences.length + summary.created.decisions.length + summary.created.tasks.length;
12062
- const totalSkipped = summary.skipped.people.length + summary.skipped.preferences.length + summary.skipped.decisions.length + summary.skipped.tasks.length;
12063
- console.log("\n\u{1F4E5} Memory Import Summary");
12064
- console.log("\u2500".repeat(40));
12065
- if (summary.created.people.length > 0) {
12066
- console.log(`\u2713 People (${summary.created.people.length}):`);
12067
- for (const name of summary.created.people.slice(0, 5)) {
12068
- console.log(` - ${name}`);
12069
- }
12070
- if (summary.created.people.length > 5) {
12071
- console.log(` ... and ${summary.created.people.length - 5} more`);
12072
- }
12073
- }
12074
- if (summary.created.preferences.length > 0) {
12075
- console.log(`\u2713 Preferences (${summary.created.preferences.length}):`);
12076
- for (const pref of summary.created.preferences.slice(0, 5)) {
12077
- console.log(` - ${pref}`);
12078
- }
12079
- if (summary.created.preferences.length > 5) {
12080
- console.log(` ... and ${summary.created.preferences.length - 5} more`);
12081
- }
12082
- }
12083
- if (summary.created.decisions.length > 0) {
12084
- console.log(`\u2713 Decisions (${summary.created.decisions.length}):`);
12085
- for (const dec of summary.created.decisions.slice(0, 5)) {
12086
- console.log(` - ${dec}`);
12087
- }
12088
- if (summary.created.decisions.length > 5) {
12089
- console.log(` ... and ${summary.created.decisions.length - 5} more`);
12090
- }
12091
- }
12092
- if (summary.created.tasks.length > 0) {
12093
- console.log(`\u2713 Tasks (${summary.created.tasks.length}):`);
12094
- for (const task of summary.created.tasks.slice(0, 5)) {
12095
- console.log(` - ${task}`);
12096
- }
12097
- if (summary.created.tasks.length > 5) {
12098
- console.log(` ... and ${summary.created.tasks.length - 5} more`);
12099
- }
12100
- }
12101
- if (totalSkipped > 0) {
12102
- console.log(`
12103
- \u2298 Skipped ${totalSkipped} items (already exist or similar)`);
12104
- }
12105
- console.log("\u2500".repeat(40));
12106
- console.log(`Total: ${totalCreated} created, ${totalSkipped} skipped`);
10075
+ return { collection: path23.basename(vaultPath), root: vaultPath };
12107
10076
  }
12108
10077
  async function setupCommand(options = {}) {
12109
10078
  const target = resolveVaultTarget(options.vault);
12110
- if (target.existed && !fs26.statSync(target.vaultPath).isDirectory()) {
10079
+ if (target.existed && !fs24.statSync(target.vaultPath).isDirectory()) {
12111
10080
  throw new Error(`Vault path is not a directory: ${target.vaultPath}`);
12112
10081
  }
12113
- if (!target.existed) fs26.mkdirSync(target.vaultPath, { recursive: true });
10082
+ if (!target.existed) fs24.mkdirSync(target.vaultPath, { recursive: true });
12114
10083
  console.log(`${target.existed ? "Found" : "Created"} vault path (${target.source}): ${target.vaultPath}`);
12115
10084
  const initialized = ensureVaultStructure(target.vaultPath);
12116
10085
  console.log(initialized ? "\u2713 Initialized vault structure." : "\u2713 Vault structure already present.");
12117
10086
  const force = options.force ?? false;
12118
10087
  const theme = options.theme ?? "neural";
12119
- if (options.from) {
12120
- const sourcePath = path26.resolve(options.from);
12121
- if (!fs26.existsSync(sourcePath)) {
12122
- throw new Error(`Source path does not exist: ${sourcePath}`);
12123
- }
12124
- console.log(`
12125
- \u{1F4C2} Scanning source: ${sourcePath}`);
12126
- const extracted = scanAndExtract(sourcePath);
12127
- const totalFound = extracted.people.length + extracted.preferences.length + extracted.decisions.length + extracted.tasks.length;
12128
- if (totalFound === 0) {
12129
- console.log("\u2298 No structured data found in source files.");
12130
- } else {
12131
- console.log(`Found: ${extracted.people.length} people, ${extracted.preferences.length} preferences, ${extracted.decisions.length} decisions, ${extracted.tasks.length} tasks`);
12132
- const summary = importToVault(target.vaultPath, extracted, force);
12133
- printImportSummary(summary);
12134
- }
12135
- }
12136
10088
  const explicitFlags = options.graphColors !== void 0 || options.bases !== void 0;
12137
- const fromOnly = options.from && !explicitFlags;
12138
- const doGraphColors = fromOnly ? false : explicitFlags ? options.graphColors !== false : true;
12139
- const doBases = fromOnly ? false : explicitFlags ? options.bases !== false : true;
10089
+ const doGraphColors = explicitFlags ? options.graphColors !== false : true;
10090
+ const doBases = explicitFlags ? options.bases !== false : true;
12140
10091
  if (doGraphColors && theme !== "none") {
12141
10092
  const wrote = writeGraphColors(target.vaultPath, theme, force);
12142
10093
  if (wrote) {
12143
10094
  console.log(`\u2713 Graph colors configured (${theme} theme)`);
12144
10095
  } else {
12145
- const obsidianDir = path26.join(target.vaultPath, ".obsidian");
12146
- if (!fs26.existsSync(obsidianDir)) {
10096
+ const obsidianDir = path23.join(target.vaultPath, ".obsidian");
10097
+ if (!fs24.existsSync(obsidianDir)) {
12147
10098
  console.log("\u2298 No .obsidian directory \u2014 skipping graph colors (not an Obsidian vault)");
12148
10099
  } else {
12149
10100
  console.log("\u2298 Graph colors already exist (use --force to overwrite)");
@@ -12163,7 +10114,7 @@ async function setupCommand(options = {}) {
12163
10114
  if (hasQmd()) {
12164
10115
  const { collection, root } = getQmdConfig(target.vaultPath);
12165
10116
  try {
12166
- (0, import_child_process5.execFileSync)("qmd", withQmdIndexArgs(["collection", "add", root, "--name", collection, "--mask", "**/*.md"], options.qmdIndexName), {
10117
+ (0, import_child_process4.execFileSync)("qmd", withQmdIndexArgs(["collection", "add", root, "--name", collection, "--mask", "**/*.md"], options.qmdIndexName), {
12167
10118
  stdio: "ignore"
12168
10119
  });
12169
10120
  console.log(`\u2713 qmd collection ready: ${collection}`);
@@ -12179,82 +10130,52 @@ async function setupCommand(options = {}) {
12179
10130
  console.log(" clawvault setup --theme neural # Neural neural graph colors");
12180
10131
  console.log(" clawvault setup --theme minimal # Subtle category colors");
12181
10132
  console.log(" clawvault setup --no-bases --no-graph-colors # Structure only");
12182
- console.log(" clawvault setup --from <path> # Import from existing memory");
12183
10133
  console.log(" clawvault setup --force # Overwrite existing configs");
12184
10134
  }
12185
10135
 
12186
10136
  // src/commands/compat.ts
12187
- var fs27 = __toESM(require("fs"), 1);
12188
- var path27 = __toESM(require("path"), 1);
10137
+ var fs25 = __toESM(require("fs"), 1);
10138
+ var path24 = __toESM(require("path"), 1);
12189
10139
  var import_gray_matter8 = __toESM(require("gray-matter"), 1);
12190
- var import_child_process6 = require("child_process");
10140
+ var import_child_process5 = require("child_process");
12191
10141
  var import_url3 = require("url");
12192
10142
  var import_meta3 = {};
12193
- var REQUIRED_HOOK_EVENTS = ["gateway:startup", "command:new", "session:start"];
12194
- var REQUIRED_HOOK_BIN = "clawvault";
12195
10143
  function readOptionalFile(filePath) {
12196
10144
  try {
12197
- if (!fs27.existsSync(filePath)) return null;
12198
- return fs27.readFileSync(filePath, "utf-8");
10145
+ if (!fs25.existsSync(filePath)) return null;
10146
+ return fs25.readFileSync(filePath, "utf-8");
12199
10147
  } catch {
12200
10148
  return null;
12201
10149
  }
12202
10150
  }
12203
10151
  function findPackageRoot() {
12204
- let dir = path27.dirname((0, import_url3.fileURLToPath)(import_meta3.url));
12205
- while (dir !== path27.dirname(dir)) {
12206
- if (fs27.existsSync(path27.join(dir, "package.json"))) {
10152
+ let dir = path24.dirname((0, import_url3.fileURLToPath)(import_meta3.url));
10153
+ while (dir !== path24.dirname(dir)) {
10154
+ if (fs25.existsSync(path24.join(dir, "package.json"))) {
12207
10155
  return dir;
12208
10156
  }
12209
- dir = path27.dirname(dir);
12210
- }
12211
- return path27.dirname((0, import_url3.fileURLToPath)(import_meta3.url));
12212
- }
12213
- function resolveOpenClawHooksDir() {
12214
- const candidates = [
12215
- path27.join(process.env.HOME || "", ".openclaw", "hooks", "clawvault"),
12216
- path27.join(process.env.OPENCLAW_HOME || "", "hooks", "clawvault"),
12217
- path27.join(process.env.OPENCLAW_STATE_DIR || "", "hooks", "clawvault")
12218
- ].filter((p) => p && !p.startsWith(path27.sep + "hooks"));
12219
- for (const candidate of candidates) {
12220
- if (fs27.existsSync(candidate)) {
12221
- return candidate;
12222
- }
10157
+ dir = path24.dirname(dir);
12223
10158
  }
12224
- return null;
10159
+ return path24.dirname((0, import_url3.fileURLToPath)(import_meta3.url));
12225
10160
  }
12226
10161
  function resolveProjectFile(relativePath, baseDir) {
12227
10162
  if (baseDir) {
12228
- return path27.resolve(baseDir, relativePath);
10163
+ return path24.resolve(baseDir, relativePath);
12229
10164
  }
12230
- const fromCwd = path27.resolve(process.cwd(), relativePath);
12231
- if (fs27.existsSync(fromCwd)) {
10165
+ const fromCwd = path24.resolve(process.cwd(), relativePath);
10166
+ if (fs25.existsSync(fromCwd)) {
12232
10167
  return fromCwd;
12233
10168
  }
12234
- if (relativePath.startsWith("hooks/clawvault/")) {
12235
- const hooksDir = resolveOpenClawHooksDir();
12236
- if (hooksDir) {
12237
- const hookRelative = relativePath.replace("hooks/clawvault/", "");
12238
- const fromHooks = path27.resolve(hooksDir, hookRelative);
12239
- if (fs27.existsSync(fromHooks)) {
12240
- return fromHooks;
12241
- }
12242
- const fromNestedHooks = path27.resolve(hooksDir, "hooks", "clawvault", hookRelative);
12243
- if (fs27.existsSync(fromNestedHooks)) {
12244
- return fromNestedHooks;
12245
- }
12246
- }
12247
- }
12248
- return path27.resolve(findPackageRoot(), relativePath);
10169
+ return path24.resolve(findPackageRoot(), relativePath);
12249
10170
  }
12250
10171
  function checkOpenClawCli() {
12251
- const result = (0, import_child_process6.spawnSync)("openclaw", ["--version"], { stdio: "ignore" });
10172
+ const result = (0, import_child_process5.spawnSync)("openclaw", ["--version"], { stdio: "ignore" });
12252
10173
  if (result.error) {
12253
10174
  return {
12254
10175
  label: "openclaw CLI available",
12255
10176
  status: "warn",
12256
10177
  detail: "openclaw binary not found",
12257
- hint: "Install OpenClaw CLI to enable hook runtime validation."
10178
+ hint: "Install OpenClaw CLI to enable plugin runtime validation."
12258
10179
  };
12259
10180
  }
12260
10181
  if (typeof result.status === "number" && result.status !== 0) {
@@ -12275,152 +10196,106 @@ function checkOpenClawCli() {
12275
10196
  }
12276
10197
  return { label: "openclaw CLI available", status: "ok" };
12277
10198
  }
12278
- function checkPackageHookRegistration(options) {
12279
- let packageRaw = readOptionalFile(resolveProjectFile("package.json", options.baseDir));
12280
- if (packageRaw && !options.baseDir) {
12281
- try {
12282
- const parsed = JSON.parse(packageRaw);
12283
- if (!parsed.openclaw?.hooks) {
12284
- const fallbackPath = path27.resolve(findPackageRoot(), "package.json");
12285
- const fallbackRaw = readOptionalFile(fallbackPath);
12286
- if (fallbackRaw) packageRaw = fallbackRaw;
12287
- }
12288
- } catch {
12289
- }
12290
- }
12291
- if (!packageRaw) {
10199
+ function checkPluginManifest(options) {
10200
+ const manifestRaw = readOptionalFile(
10201
+ resolveProjectFile("openclaw.plugin.json", options.baseDir)
10202
+ );
10203
+ if (!manifestRaw) {
12292
10204
  return {
12293
- label: "package hook registration",
10205
+ label: "plugin manifest",
12294
10206
  status: "error",
12295
- detail: "package.json not found"
10207
+ detail: "openclaw.plugin.json not found",
10208
+ hint: "Create openclaw.plugin.json with id, kind, and configSchema fields."
12296
10209
  };
12297
10210
  }
12298
10211
  try {
12299
- const parsed = JSON.parse(packageRaw);
12300
- const registeredHooks = parsed.openclaw?.hooks ?? [];
12301
- if (registeredHooks.includes("./hooks/clawvault")) {
10212
+ const manifest = JSON.parse(manifestRaw);
10213
+ const issues = [];
10214
+ if (!manifest.id) issues.push("missing id");
10215
+ if (!manifest.kind) issues.push("missing kind");
10216
+ if (!manifest.configSchema) issues.push("missing configSchema");
10217
+ if (issues.length > 0) {
12302
10218
  return {
12303
- label: "package hook registration",
12304
- status: "ok",
12305
- detail: "./hooks/clawvault"
10219
+ label: "plugin manifest",
10220
+ status: "error",
10221
+ detail: issues.join(", ")
12306
10222
  };
12307
10223
  }
12308
10224
  return {
12309
- label: "package hook registration",
12310
- status: "error",
12311
- detail: "Missing ./hooks/clawvault in package openclaw.hooks"
10225
+ label: "plugin manifest",
10226
+ status: "ok",
10227
+ detail: `id=${manifest.id} kind=${manifest.kind}`
12312
10228
  };
12313
10229
  } catch (err) {
12314
10230
  return {
12315
- label: "package hook registration",
10231
+ label: "plugin manifest",
12316
10232
  status: "error",
12317
- detail: err?.message || "Unable to parse package.json"
10233
+ detail: err?.message || "Unable to parse openclaw.plugin.json"
12318
10234
  };
12319
10235
  }
12320
10236
  }
12321
- function checkHookManifest(options) {
12322
- const hookRaw = readOptionalFile(resolveProjectFile("hooks/clawvault/HOOK.md", options.baseDir));
12323
- if (!hookRaw) {
10237
+ function checkPluginExtensions(options) {
10238
+ let packageRaw = readOptionalFile(
10239
+ resolveProjectFile("package.json", options.baseDir)
10240
+ );
10241
+ if (packageRaw && !options.baseDir) {
10242
+ try {
10243
+ const parsed = JSON.parse(packageRaw);
10244
+ if (!parsed.openclaw?.extensions) {
10245
+ const fallbackPath = path24.resolve(findPackageRoot(), "package.json");
10246
+ const fallbackRaw = readOptionalFile(fallbackPath);
10247
+ if (fallbackRaw) packageRaw = fallbackRaw;
10248
+ }
10249
+ } catch {
10250
+ }
10251
+ }
10252
+ if (!packageRaw) {
12324
10253
  return {
12325
- label: "hook manifest",
10254
+ label: "plugin extensions registration",
12326
10255
  status: "error",
12327
- detail: "HOOK.md not found"
10256
+ detail: "package.json not found"
12328
10257
  };
12329
10258
  }
12330
10259
  try {
12331
- const parsed = (0, import_gray_matter8.default)(hookRaw);
12332
- const openclaw = parsed.data?.metadata?.openclaw;
12333
- const events = Array.isArray(openclaw?.events) ? openclaw?.events ?? [] : [];
12334
- const missingEvents = REQUIRED_HOOK_EVENTS.filter((event) => !events.includes(event));
12335
- if (missingEvents.length === 0) {
10260
+ const parsed = JSON.parse(packageRaw);
10261
+ const extensions = parsed.openclaw?.extensions ?? [];
10262
+ if (extensions.length === 0) {
12336
10263
  return {
12337
- label: "hook manifest events",
12338
- status: "ok",
12339
- detail: events.join(", ")
10264
+ label: "plugin extensions registration",
10265
+ status: "error",
10266
+ detail: "Missing openclaw.extensions in package.json",
10267
+ hint: 'Add openclaw.extensions: ["./dist/plugin/index.js"] to package.json.'
12340
10268
  };
12341
10269
  }
12342
- return {
12343
- label: "hook manifest events",
12344
- status: "error",
12345
- detail: `Missing events: ${missingEvents.join(", ")}`
12346
- };
12347
- } catch (err) {
12348
- return {
12349
- label: "hook manifest events",
12350
- status: "error",
12351
- detail: err?.message || "Unable to parse HOOK.md frontmatter"
12352
- };
12353
- }
12354
- }
12355
- function checkHookManifestRequirements(options) {
12356
- const hookRaw = readOptionalFile(resolveProjectFile("hooks/clawvault/HOOK.md", options.baseDir));
12357
- if (!hookRaw) {
12358
- return {
12359
- label: "hook manifest requirements",
12360
- status: "error",
12361
- detail: "HOOK.md not found"
12362
- };
12363
- }
12364
- try {
12365
- const parsed = (0, import_gray_matter8.default)(hookRaw);
12366
- const requiresBins = parsed.data?.metadata?.openclaw?.requires?.bins;
12367
- const bins = Array.isArray(requiresBins) ? requiresBins : [];
12368
- if (bins.includes(REQUIRED_HOOK_BIN)) {
10270
+ const baseDir = options.baseDir || findPackageRoot();
10271
+ const missing = extensions.filter(
10272
+ (ext) => !fs25.existsSync(path24.resolve(baseDir, ext))
10273
+ );
10274
+ if (missing.length > 0) {
12369
10275
  return {
12370
- label: "hook manifest requirements",
12371
- status: "ok",
12372
- detail: `bins: ${bins.join(", ")}`
10276
+ label: "plugin extensions registration",
10277
+ status: "error",
10278
+ detail: `Entry file(s) not found: ${missing.join(", ")}`,
10279
+ hint: "Run npm run build to generate dist files."
12373
10280
  };
12374
10281
  }
12375
10282
  return {
12376
- label: "hook manifest requirements",
12377
- status: "warn",
12378
- detail: `Missing required hook bin "${REQUIRED_HOOK_BIN}"`,
12379
- hint: 'Add metadata.openclaw.requires.bins: ["clawvault"] to hooks/clawvault/HOOK.md.'
10283
+ label: "plugin extensions registration",
10284
+ status: "ok",
10285
+ detail: extensions.join(", ")
12380
10286
  };
12381
10287
  } catch (err) {
12382
10288
  return {
12383
- label: "hook manifest requirements",
12384
- status: "error",
12385
- detail: err?.message || "Unable to parse HOOK.md frontmatter"
12386
- };
12387
- }
12388
- }
12389
- function checkHookHandlerSafety(options) {
12390
- const handlerRaw = readOptionalFile(resolveProjectFile("hooks/clawvault/handler.js", options.baseDir));
12391
- if (!handlerRaw) {
12392
- return {
12393
- label: "hook handler script",
10289
+ label: "plugin extensions registration",
12394
10290
  status: "error",
12395
- detail: "handler.js not found"
12396
- };
12397
- }
12398
- const usesExecFileSync = handlerRaw.includes("execFileSync");
12399
- const usesExecSync = /\bexecSync\b/.test(handlerRaw);
12400
- const enablesShell = /\bshell\s*:\s*true\b/.test(handlerRaw);
12401
- const delegatesAutoProfile = /['"]--profile['"]\s*,\s*['"]auto['"]/.test(handlerRaw);
12402
- const violations = [];
12403
- if (!usesExecFileSync || usesExecSync) {
12404
- violations.push("execFileSync-only execution path");
12405
- }
12406
- if (enablesShell) {
12407
- violations.push("shell:false execution option");
12408
- }
12409
- if (!delegatesAutoProfile) {
12410
- violations.push("shared context profile delegation (--profile auto)");
12411
- }
12412
- if (violations.length > 0) {
12413
- return {
12414
- label: "hook handler safety",
12415
- status: "warn",
12416
- detail: `Missing conventions: ${violations.join(", ")}`,
12417
- hint: "Use execFileSync (no shell), avoid execSync, and delegate profile inference via --profile auto."
10291
+ detail: err?.message || "Unable to parse package.json"
12418
10292
  };
12419
10293
  }
12420
- return { label: "hook handler safety", status: "ok" };
12421
10294
  }
12422
10295
  function checkSkillMetadata(options) {
12423
- const skillRaw = readOptionalFile(resolveProjectFile("SKILL.md", options.baseDir));
10296
+ const skillRaw = readOptionalFile(
10297
+ resolveProjectFile("SKILL.md", options.baseDir)
10298
+ );
12424
10299
  if (!skillRaw) {
12425
10300
  return {
12426
10301
  label: "skill metadata",
@@ -12459,10 +10334,8 @@ function checkSkillMetadata(options) {
12459
10334
  function checkOpenClawCompatibility(options = {}) {
12460
10335
  const checks = [
12461
10336
  checkOpenClawCli(),
12462
- checkPackageHookRegistration(options),
12463
- checkHookManifest(options),
12464
- checkHookManifestRequirements(options),
12465
- checkHookHandlerSafety(options),
10337
+ checkPluginManifest(options),
10338
+ checkPluginExtensions(options),
12466
10339
  checkSkillMetadata(options)
12467
10340
  ];
12468
10341
  const warnings = checks.filter((check) => check.status === "warn").length;
@@ -12482,7 +10355,9 @@ function formatCompatibilityReport(report) {
12482
10355
  lines.push("");
12483
10356
  for (const check of report.checks) {
12484
10357
  const prefix = check.status === "ok" ? "\u2713" : check.status === "warn" ? "\u26A0" : "\u2717";
12485
- lines.push(`${prefix} ${check.label}${check.detail ? ` \u2014 ${check.detail}` : ""}`);
10358
+ lines.push(
10359
+ `${prefix} ${check.label}${check.detail ? ` \u2014 ${check.detail}` : ""}`
10360
+ );
12486
10361
  if (check.hint) {
12487
10362
  lines.push(` ${check.hint}`);
12488
10363
  }
@@ -12556,8 +10431,8 @@ async function graphCommand(options = {}) {
12556
10431
  }
12557
10432
 
12558
10433
  // src/commands/kanban.ts
12559
- var fs28 = __toESM(require("fs"), 1);
12560
- var path28 = __toESM(require("path"), 1);
10434
+ var fs26 = __toESM(require("fs"), 1);
10435
+ var path25 = __toESM(require("path"), 1);
12561
10436
  var import_gray_matter9 = __toESM(require("gray-matter"), 1);
12562
10437
  var STATUS_LANES = [
12563
10438
  { status: "open", name: "Open" },
@@ -12586,14 +10461,14 @@ function normalizeGroupBy(value) {
12586
10461
  throw new Error(`Unsupported kanban group field: ${normalized}`);
12587
10462
  }
12588
10463
  function resolveBoardPath(vaultPath, output) {
12589
- const resolvedVaultPath = path28.resolve(vaultPath);
10464
+ const resolvedVaultPath = path25.resolve(vaultPath);
12590
10465
  if (!output) {
12591
- return path28.join(resolvedVaultPath, "Board.md");
10466
+ return path25.join(resolvedVaultPath, "Board.md");
12592
10467
  }
12593
- if (path28.isAbsolute(output)) {
10468
+ if (path25.isAbsolute(output)) {
12594
10469
  return output;
12595
10470
  }
12596
- return path28.join(resolvedVaultPath, output);
10471
+ return path25.join(resolvedVaultPath, output);
12597
10472
  }
12598
10473
  function toHashTag(value) {
12599
10474
  return value.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-9/_-]/g, "");
@@ -12745,7 +10620,7 @@ function syncKanbanBoard(vaultPath, options = {}) {
12745
10620
  groupBy,
12746
10621
  now: options.now
12747
10622
  });
12748
- fs28.writeFileSync(outputPath, markdown);
10623
+ fs26.writeFileSync(outputPath, markdown);
12749
10624
  return {
12750
10625
  outputPath,
12751
10626
  groupBy,
@@ -12828,10 +10703,10 @@ function hasUpdates(updates) {
12828
10703
  }
12829
10704
  function importKanbanBoard(vaultPath, options = {}) {
12830
10705
  const outputPath = resolveBoardPath(vaultPath, options.output);
12831
- if (!fs28.existsSync(outputPath)) {
10706
+ if (!fs26.existsSync(outputPath)) {
12832
10707
  throw new Error(`Kanban board not found: ${outputPath}`);
12833
10708
  }
12834
- const markdown = fs28.readFileSync(outputPath, "utf-8");
10709
+ const markdown = fs26.readFileSync(outputPath, "utf-8");
12835
10710
  const parsed = parseKanbanMarkdown(markdown);
12836
10711
  const changes = [];
12837
10712
  const missingSlugs = [];
@@ -12973,7 +10848,7 @@ function registerArchiveCommand(program) {
12973
10848
  }
12974
10849
 
12975
10850
  // src/commands/rebuild.ts
12976
- var fs29 = __toESM(require("fs"), 1);
10851
+ var fs27 = __toESM(require("fs"), 1);
12977
10852
  var DATE_RE2 = /^\d{4}-\d{2}-\d{2}$/;
12978
10853
  function parseDateFlag(raw, label) {
12979
10854
  if (!raw) return void 0;
@@ -12984,7 +10859,7 @@ function parseDateFlag(raw, label) {
12984
10859
  return trimmed;
12985
10860
  }
12986
10861
  function loadRawMessages(rawFilePath) {
12987
- const lines = fs29.readFileSync(rawFilePath, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
10862
+ const lines = fs27.readFileSync(rawFilePath, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
12988
10863
  const messages = [];
12989
10864
  for (const line of lines) {
12990
10865
  try {
@@ -13025,8 +10900,8 @@ async function rebuildCommand(options) {
13025
10900
  for (const date of dates) {
13026
10901
  const ledgerObservationPath = getObservationPath(vaultPath, date);
13027
10902
  const legacyObservationPath = getLegacyObservationPath(vaultPath, date);
13028
- fs29.rmSync(ledgerObservationPath, { force: true });
13029
- fs29.rmSync(legacyObservationPath, { force: true });
10903
+ fs27.rmSync(ledgerObservationPath, { force: true });
10904
+ fs27.rmSync(legacyObservationPath, { force: true });
13030
10905
  const fixedNow = () => /* @__PURE__ */ new Date(`${date}T12:00:00.000Z`);
13031
10906
  const observer = new Observer(vaultPath, {
13032
10907
  tokenThreshold: 1,
@@ -13061,29 +10936,29 @@ function registerRebuildCommand(program) {
13061
10936
  }
13062
10937
 
13063
10938
  // src/commands/doctor.ts
13064
- var fs32 = __toESM(require("fs"), 1);
10939
+ var fs30 = __toESM(require("fs"), 1);
13065
10940
  var os3 = __toESM(require("os"), 1);
13066
- var path31 = __toESM(require("path"), 1);
10941
+ var path28 = __toESM(require("path"), 1);
13067
10942
 
13068
10943
  // src/lib/backlinks.ts
13069
- var fs31 = __toESM(require("fs"), 1);
13070
- var path30 = __toESM(require("path"), 1);
10944
+ var fs29 = __toESM(require("fs"), 1);
10945
+ var path27 = __toESM(require("path"), 1);
13071
10946
 
13072
10947
  // src/lib/entity-index.ts
13073
- var fs30 = __toESM(require("fs"), 1);
13074
- var path29 = __toESM(require("path"), 1);
10948
+ var fs28 = __toESM(require("fs"), 1);
10949
+ var path26 = __toESM(require("path"), 1);
13075
10950
  var import_gray_matter10 = __toESM(require("gray-matter"), 1);
13076
10951
  function buildEntityIndex(vaultPath) {
13077
10952
  const entries = /* @__PURE__ */ new Map();
13078
10953
  const byPath = /* @__PURE__ */ new Map();
13079
10954
  const entityFolders = ["people", "projects", "agents", "lessons", "decisions", "commitments"];
13080
10955
  for (const folder of entityFolders) {
13081
- const folderPath = path29.join(vaultPath, folder);
13082
- if (!fs30.existsSync(folderPath)) continue;
13083
- const files = fs30.readdirSync(folderPath).filter((f) => f.endsWith(".md"));
10956
+ const folderPath = path26.join(vaultPath, folder);
10957
+ if (!fs28.existsSync(folderPath)) continue;
10958
+ const files = fs28.readdirSync(folderPath).filter((f) => f.endsWith(".md"));
13084
10959
  for (const file of files) {
13085
- const filePath = path29.join(folderPath, file);
13086
- const content = fs30.readFileSync(filePath, "utf-8");
10960
+ const filePath = path26.join(folderPath, file);
10961
+ const content = fs28.readFileSync(filePath, "utf-8");
13087
10962
  const { data: frontmatter } = (0, import_gray_matter10.default)(content);
13088
10963
  const relativePath = `${folder}/${file.replace(".md", "")}`;
13089
10964
  const baseName = file.replace(".md", "");
@@ -13109,8 +10984,8 @@ function buildEntityIndex(vaultPath) {
13109
10984
  // src/lib/backlinks.ts
13110
10985
  var WIKI_LINK_REGEX = /\[\[([^\]]+)\]\]/g;
13111
10986
  function toVaultId(vaultPath, filePath) {
13112
- const relative6 = path30.relative(vaultPath, filePath).replace(/\.md$/, "");
13113
- return relative6.split(path30.sep).join("/");
10987
+ const relative5 = path27.relative(vaultPath, filePath).replace(/\.md$/, "");
10988
+ return relative5.split(path27.sep).join("/");
13114
10989
  }
13115
10990
  function normalizeLinkTarget(raw) {
13116
10991
  let target = raw.trim();
@@ -13141,9 +11016,9 @@ function listMarkdownFiles(vaultPath) {
13141
11016
  const files = [];
13142
11017
  const skipDirs = /* @__PURE__ */ new Set(["archive", "templates", "node_modules"]);
13143
11018
  function walk(dir) {
13144
- const entries = fs31.readdirSync(dir, { withFileTypes: true });
11019
+ const entries = fs29.readdirSync(dir, { withFileTypes: true });
13145
11020
  for (const entry of entries) {
13146
- const fullPath = path30.join(dir, entry.name);
11021
+ const fullPath = path27.join(dir, entry.name);
13147
11022
  if (entry.isDirectory()) {
13148
11023
  if (entry.name.startsWith(".") || skipDirs.has(entry.name)) continue;
13149
11024
  walk(fullPath);
@@ -13185,7 +11060,7 @@ function scanVaultLinks(vaultPath, options = {}) {
13185
11060
  let linkCount = 0;
13186
11061
  for (const file of files) {
13187
11062
  const sourceId = toVaultId(vaultPath, file);
13188
- const content = fs31.readFileSync(file, "utf-8");
11063
+ const content = fs29.readFileSync(file, "utf-8");
13189
11064
  const matches = content.match(WIKI_LINK_REGEX) || [];
13190
11065
  linkCount += matches.length;
13191
11066
  for (const match of matches) {
@@ -13234,12 +11109,12 @@ function describeAge(date, now = Date.now()) {
13234
11109
  return formatAge(now - date.getTime());
13235
11110
  }
13236
11111
  function loadCheckpointTimestamp(vaultPath) {
13237
- const checkpointPath = path31.join(vaultPath, CLAWVAULT_DIR, CHECKPOINT_FILE);
13238
- if (!fs32.existsSync(checkpointPath)) {
11112
+ const checkpointPath = path28.join(vaultPath, CLAWVAULT_DIR, CHECKPOINT_FILE);
11113
+ if (!fs30.existsSync(checkpointPath)) {
13239
11114
  return {};
13240
11115
  }
13241
11116
  try {
13242
- const data = JSON.parse(fs32.readFileSync(checkpointPath, "utf-8"));
11117
+ const data = JSON.parse(fs30.readFileSync(checkpointPath, "utf-8"));
13243
11118
  return { timestamp: data.timestamp };
13244
11119
  } catch (err) {
13245
11120
  return { error: err?.message || "Failed to parse checkpoint" };
@@ -13247,20 +11122,20 @@ function loadCheckpointTimestamp(vaultPath) {
13247
11122
  }
13248
11123
  function getShellConfigPaths(shellPath) {
13249
11124
  const home = os3.homedir();
13250
- const shellName = shellPath ? path31.basename(shellPath) : "bash";
11125
+ const shellName = shellPath ? path28.basename(shellPath) : "bash";
13251
11126
  if (shellName === "zsh") {
13252
- return [path31.join(home, ".zshenv"), path31.join(home, ".zshrc"), path31.join(home, ".zprofile")];
11127
+ return [path28.join(home, ".zshenv"), path28.join(home, ".zshrc"), path28.join(home, ".zprofile")];
13253
11128
  }
13254
11129
  if (shellName === "fish") {
13255
- return [path31.join(home, ".config", "fish", "config.fish")];
11130
+ return [path28.join(home, ".config", "fish", "config.fish")];
13256
11131
  }
13257
- return [path31.join(home, ".bashrc"), path31.join(home, ".bash_profile"), path31.join(home, ".profile")];
11132
+ return [path28.join(home, ".bashrc"), path28.join(home, ".bash_profile"), path28.join(home, ".profile")];
13258
11133
  }
13259
11134
  function hasClawvaultPathConfig(paths) {
13260
11135
  for (const filePath of paths) {
13261
- if (!fs32.existsSync(filePath)) continue;
11136
+ if (!fs30.existsSync(filePath)) continue;
13262
11137
  try {
13263
- const content = fs32.readFileSync(filePath, "utf-8");
11138
+ const content = fs30.readFileSync(filePath, "utf-8");
13264
11139
  if (/CLAWVAULT_PATH\s*=/.test(content)) {
13265
11140
  return true;
13266
11141
  }
@@ -13271,13 +11146,13 @@ function hasClawvaultPathConfig(paths) {
13271
11146
  }
13272
11147
  async function resolveVault(vaultPath) {
13273
11148
  if (vaultPath) {
13274
- const vault = new ClawVault(path31.resolve(vaultPath));
11149
+ const vault = new ClawVault(path28.resolve(vaultPath));
13275
11150
  await vault.load();
13276
11151
  return vault;
13277
11152
  }
13278
11153
  const envPath = process.env.CLAWVAULT_PATH;
13279
11154
  if (envPath) {
13280
- const vault = new ClawVault(path31.resolve(envPath));
11155
+ const vault = new ClawVault(path28.resolve(envPath));
13281
11156
  await vault.load();
13282
11157
  return vault;
13283
11158
  }
@@ -13326,12 +11201,12 @@ async function doctor(options) {
13326
11201
  });
13327
11202
  errors++;
13328
11203
  }
13329
- const shellConfigs = getShellConfigPaths(process.env.SHELL).filter(fs32.existsSync);
11204
+ const shellConfigs = getShellConfigPaths(process.env.SHELL).filter(fs30.existsSync);
13330
11205
  if (hasClawvaultPathConfig(shellConfigs)) {
13331
11206
  checks.push({
13332
11207
  label: "CLAWVAULT_PATH in shell config",
13333
11208
  status: "ok",
13334
- detail: shellConfigs.map((p) => path31.basename(p)).join(", ")
11209
+ detail: shellConfigs.map((p) => path28.basename(p)).join(", ")
13335
11210
  });
13336
11211
  } else {
13337
11212
  checks.push({
@@ -13529,8 +11404,8 @@ async function doctor(options) {
13529
11404
  }
13530
11405
 
13531
11406
  // src/commands/replay.ts
13532
- var fs33 = __toESM(require("fs"), 1);
13533
- var path32 = __toESM(require("path"), 1);
11407
+ var fs31 = __toESM(require("fs"), 1);
11408
+ var path29 = __toESM(require("path"), 1);
13534
11409
 
13535
11410
  // src/replay/normalizers/chatgpt.ts
13536
11411
  function normalizeText3(value) {
@@ -13833,10 +11708,10 @@ function parseDateFlag2(raw, label) {
13833
11708
  return trimmed;
13834
11709
  }
13835
11710
  function collectFiles(rootPath, predicate) {
13836
- if (!fs33.existsSync(rootPath)) {
11711
+ if (!fs31.existsSync(rootPath)) {
13837
11712
  return [];
13838
11713
  }
13839
- const stat = fs33.statSync(rootPath);
11714
+ const stat = fs31.statSync(rootPath);
13840
11715
  if (stat.isFile()) {
13841
11716
  return predicate(rootPath) ? [rootPath] : [];
13842
11717
  }
@@ -13844,8 +11719,8 @@ function collectFiles(rootPath, predicate) {
13844
11719
  return [];
13845
11720
  }
13846
11721
  const files = [];
13847
- for (const entry of fs33.readdirSync(rootPath, { withFileTypes: true })) {
13848
- const absolute = path32.join(rootPath, entry.name);
11722
+ for (const entry of fs31.readdirSync(rootPath, { withFileTypes: true })) {
11723
+ const absolute = path29.join(rootPath, entry.name);
13849
11724
  if (entry.isDirectory()) {
13850
11725
  files.push(...collectFiles(absolute, predicate));
13851
11726
  continue;
@@ -13857,11 +11732,11 @@ function collectFiles(rootPath, predicate) {
13857
11732
  return files.sort((left, right) => left.localeCompare(right));
13858
11733
  }
13859
11734
  function loadJson(filePath) {
13860
- return JSON.parse(fs33.readFileSync(filePath, "utf-8"));
11735
+ return JSON.parse(fs31.readFileSync(filePath, "utf-8"));
13861
11736
  }
13862
11737
  function normalizeReplayMessages(source, inputPath) {
13863
11738
  if (source === "chatgpt") {
13864
- const files2 = collectFiles(inputPath, (filePath) => path32.basename(filePath).toLowerCase() === "conversations.json");
11739
+ const files2 = collectFiles(inputPath, (filePath) => path29.basename(filePath).toLowerCase() === "conversations.json");
13865
11740
  if (files2.length === 0) {
13866
11741
  throw new Error("ChatGPT replay expects conversations.json in --input path.");
13867
11742
  }
@@ -13884,7 +11759,7 @@ function normalizeReplayMessages(source, inputPath) {
13884
11759
  }
13885
11760
  return files2.flatMap((filePath) => {
13886
11761
  if (filePath.toLowerCase().endsWith(".jsonl")) {
13887
- return normalizeOpenCodeExport(fs33.readFileSync(filePath, "utf-8"));
11762
+ return normalizeOpenCodeExport(fs31.readFileSync(filePath, "utf-8"));
13888
11763
  }
13889
11764
  return normalizeOpenCodeExport(loadJson(filePath));
13890
11765
  });
@@ -13893,7 +11768,7 @@ function normalizeReplayMessages(source, inputPath) {
13893
11768
  if (files.length === 0) {
13894
11769
  throw new Error("OpenClaw replay expects .jsonl session transcript files.");
13895
11770
  }
13896
- return files.flatMap((filePath) => normalizeOpenClawTranscript(fs33.readFileSync(filePath, "utf-8")));
11771
+ return files.flatMap((filePath) => normalizeOpenClawTranscript(fs31.readFileSync(filePath, "utf-8")));
13897
11772
  }
13898
11773
  function normalizeDateFromTimestamp(timestamp, fallbackDate) {
13899
11774
  if (!timestamp) return fallbackDate;
@@ -13914,8 +11789,8 @@ async function replayCommand(options) {
13914
11789
  throw new Error(`Invalid range: --from ${fromDate} is after --to ${toDate}.`);
13915
11790
  }
13916
11791
  const vaultPath = resolveVaultPath({ explicitPath: options.vaultPath });
13917
- const resolvedInput = path32.resolve(options.inputPath);
13918
- if (!fs33.existsSync(resolvedInput)) {
11792
+ const resolvedInput = path29.resolve(options.inputPath);
11793
+ if (!fs31.existsSync(resolvedInput)) {
13919
11794
  throw new Error(`Replay input path not found: ${resolvedInput}`);
13920
11795
  }
13921
11796
  const allMessages = normalizeReplayMessages(source, resolvedInput);
@@ -13959,7 +11834,7 @@ async function replayCommand(options) {
13959
11834
  }
13960
11835
  await observer.processMessages(messages, {
13961
11836
  source,
13962
- transcriptId: path32.basename(resolvedInput),
11837
+ transcriptId: path29.basename(resolvedInput),
13963
11838
  timestamp: nowForDate()
13964
11839
  });
13965
11840
  await observer.flush();
@@ -13992,7 +11867,7 @@ function registerReplayCommand(program) {
13992
11867
  }
13993
11868
 
13994
11869
  // src/commands/migrate-observations.ts
13995
- var fs34 = __toESM(require("fs"), 1);
11870
+ var fs32 = __toESM(require("fs"), 1);
13996
11871
  function toBackupPath(filePath) {
13997
11872
  if (filePath.toLowerCase().endsWith(".md")) {
13998
11873
  return `${filePath.slice(0, -3)}.emoji-backup.md`;
@@ -14039,7 +11914,7 @@ function migrateObservations(vaultPath, options = {}) {
14039
11914
  let migrated = 0;
14040
11915
  let backups = 0;
14041
11916
  for (const entry of files) {
14042
- const raw = fs34.readFileSync(entry.path, "utf-8");
11917
+ const raw = fs32.readFileSync(entry.path, "utf-8");
14043
11918
  const { converted, changed } = convertObservationMarkdown(raw);
14044
11919
  if (!changed) {
14045
11920
  continue;
@@ -14049,11 +11924,11 @@ function migrateObservations(vaultPath, options = {}) {
14049
11924
  continue;
14050
11925
  }
14051
11926
  const backupPath = toBackupPath(entry.path);
14052
- if (!fs34.existsSync(backupPath)) {
14053
- fs34.copyFileSync(entry.path, backupPath);
11927
+ if (!fs32.existsSync(backupPath)) {
11928
+ fs32.copyFileSync(entry.path, backupPath);
14054
11929
  backups += 1;
14055
11930
  }
14056
- fs34.writeFileSync(entry.path, `${converted.trim()}
11931
+ fs32.writeFileSync(entry.path, `${converted.trim()}
14057
11932
  `, "utf-8");
14058
11933
  }
14059
11934
  return {
@@ -14086,8 +11961,8 @@ function registerMigrateObservationsCommand(program) {
14086
11961
  }
14087
11962
 
14088
11963
  // src/commands/session-recap.ts
14089
- var fs35 = __toESM(require("fs"), 1);
14090
- var path33 = __toESM(require("path"), 1);
11964
+ var fs33 = __toESM(require("fs"), 1);
11965
+ var path30 = __toESM(require("path"), 1);
14091
11966
  var DEFAULT_LIMIT2 = 15;
14092
11967
  var MAX_LIMIT = 50;
14093
11968
  var READ_CHUNK_SIZE = 64 * 1024;
@@ -14130,33 +12005,33 @@ function normalizeLimit(limit) {
14130
12005
  return Math.min(MAX_LIMIT, Math.max(1, parsed));
14131
12006
  }
14132
12007
  function isPathInside(parentPath, candidatePath) {
14133
- const normalizedParent = parentPath.endsWith(path33.sep) ? parentPath : `${parentPath}${path33.sep}`;
12008
+ const normalizedParent = parentPath.endsWith(path30.sep) ? parentPath : `${parentPath}${path30.sep}`;
14134
12009
  return candidatePath.startsWith(normalizedParent);
14135
12010
  }
14136
12011
  function resolveSafeTranscriptPath(agentId, sessionId, sessionFile) {
14137
12012
  const sessionsDir = getSessionsDir(agentId);
14138
- if (!fs35.existsSync(sessionsDir)) {
12013
+ if (!fs33.existsSync(sessionsDir)) {
14139
12014
  throw new Error(`Sessions directory not found for agent "${agentId}".`);
14140
12015
  }
14141
- const sessionsDirRealPath = fs35.realpathSync(sessionsDir);
12016
+ const sessionsDirRealPath = fs33.realpathSync(sessionsDir);
14142
12017
  const candidatePaths = [];
14143
12018
  if (typeof sessionFile === "string" && sessionFile.trim()) {
14144
- candidatePaths.push(path33.resolve(sessionFile));
12019
+ candidatePaths.push(path30.resolve(sessionFile));
14145
12020
  }
14146
12021
  candidatePaths.push(getSessionFilePath(agentId, sessionId));
14147
12022
  for (const candidatePath of candidatePaths) {
14148
- if (path33.extname(candidatePath).toLowerCase() !== ".jsonl") continue;
14149
- if (!fs35.existsSync(candidatePath)) continue;
12023
+ if (path30.extname(candidatePath).toLowerCase() !== ".jsonl") continue;
12024
+ if (!fs33.existsSync(candidatePath)) continue;
14150
12025
  let candidateRealPath = "";
14151
12026
  try {
14152
- candidateRealPath = fs35.realpathSync(candidatePath);
12027
+ candidateRealPath = fs33.realpathSync(candidatePath);
14153
12028
  } catch {
14154
12029
  continue;
14155
12030
  }
14156
12031
  if (!isPathInside(sessionsDirRealPath, candidateRealPath)) {
14157
12032
  continue;
14158
12033
  }
14159
- const stat = fs35.statSync(candidateRealPath);
12034
+ const stat = fs33.statSync(candidateRealPath);
14160
12035
  if (!stat.isFile()) continue;
14161
12036
  return candidateRealPath;
14162
12037
  }
@@ -14249,16 +12124,16 @@ function applyOutputBudget(turns) {
14249
12124
  }
14250
12125
  function readRecentTurnsFromTranscript(filePath, limit) {
14251
12126
  if (limit <= 0) return [];
14252
- const fileHandle = fs35.openSync(filePath, "r");
12127
+ const fileHandle = fs33.openSync(filePath, "r");
14253
12128
  const collected = [];
14254
12129
  let remainder = "";
14255
12130
  try {
14256
- let position = fs35.fstatSync(fileHandle).size;
12131
+ let position = fs33.fstatSync(fileHandle).size;
14257
12132
  while (position > 0 && collected.length < limit) {
14258
12133
  const readSize = Math.min(READ_CHUNK_SIZE, position);
14259
12134
  position -= readSize;
14260
12135
  const buffer = Buffer.allocUnsafe(readSize);
14261
- fs35.readSync(fileHandle, buffer, 0, readSize, position);
12136
+ fs33.readSync(fileHandle, buffer, 0, readSize, position);
14262
12137
  const chunk = buffer.toString("utf-8");
14263
12138
  const text = chunk + remainder;
14264
12139
  const lines = text.split("\n");
@@ -14274,7 +12149,7 @@ function readRecentTurnsFromTranscript(filePath, limit) {
14274
12149
  if (turn) collected.push(turn);
14275
12150
  }
14276
12151
  } finally {
14277
- fs35.closeSync(fileHandle);
12152
+ fs33.closeSync(fileHandle);
14278
12153
  }
14279
12154
  return applyOutputBudget(collected.reverse());
14280
12155
  }
@@ -14352,7 +12227,7 @@ var import_meta4 = {};
14352
12227
  function readPackageVersion() {
14353
12228
  try {
14354
12229
  const pkgUrl = new URL("../package.json", import_meta4.url);
14355
- const pkg = JSON.parse(fs36.readFileSync(pkgUrl, "utf-8"));
12230
+ const pkg = JSON.parse(fs34.readFileSync(pkgUrl, "utf-8"));
14356
12231
  return pkg.version ?? "0.0.0";
14357
12232
  } catch {
14358
12233
  return "0.0.0";
@@ -14364,12 +12239,10 @@ function registerCommanderCommands(program) {
14364
12239
  }
14365
12240
  // Annotate the CommonJS export names for ESM import in node:
14366
12241
  0 && (module.exports = {
14367
- CLAWVAULT_SERVE_PATH,
14368
12242
  ClawVault,
14369
12243
  Compressor,
14370
12244
  DEFAULT_CATEGORIES,
14371
12245
  DEFAULT_CONFIG,
14372
- DEFAULT_SERVE_PORT,
14373
12246
  MEMORY_GRAPH_SCHEMA_VERSION,
14374
12247
  MEMORY_TYPES,
14375
12248
  Observer,
@@ -14396,19 +12269,15 @@ function registerCommanderCommands(program) {
14396
12269
  buildTemplateVariables,
14397
12270
  buildTransitionEvent,
14398
12271
  checkOpenClawCompatibility,
14399
- checkPeerClawVault,
14400
12272
  classifyQuestion,
14401
- compareManifests,
14402
12273
  compatCommand,
14403
12274
  compatibilityExitCode,
14404
12275
  completeTask,
14405
- configureTailscaleServe,
14406
12276
  contextCommand,
14407
12277
  countBlockedTransitions,
14408
12278
  createProject,
14409
12279
  createVault,
14410
12280
  deterministicInjectMatches,
14411
- discoverClawVaultPeers,
14412
12281
  doctor,
14413
12282
  embedCommand,
14414
12283
  entitySimilarity,
@@ -14418,34 +12287,26 @@ function registerCommanderCommands(program) {
14418
12287
  extractPreferences,
14419
12288
  extractTags,
14420
12289
  extractWikiLinks,
14421
- fetchRemoteFile,
14422
- fetchRemoteManifest,
14423
12290
  filterSuperseded,
14424
12291
  findNearestVaultPath,
14425
- findPeer,
14426
12292
  findVault,
14427
12293
  formatContextMarkdown,
14428
12294
  formatKanbanCard,
14429
12295
  formatSessionRecapMarkdown,
14430
12296
  formatTransitionsTable,
14431
12297
  generateKanbanMarkdown,
14432
- generateVaultManifest,
14433
12298
  getConfig,
14434
12299
  getConfigValue,
14435
12300
  getMemoryGraph,
14436
12301
  getObserverStaleness,
14437
- getOnlinePeers,
14438
12302
  getProjectActivity,
14439
12303
  getProjectTasks,
14440
12304
  getScaledObservationThresholdBytes,
14441
12305
  getSupersessionInfo,
14442
- getTailscaleStatus,
14443
- getTailscaleVersion,
14444
12306
  getVaultPath,
14445
12307
  graphCommand,
14446
12308
  graphSummary,
14447
12309
  hasQmd,
14448
- hasTailscale,
14449
12310
  importKanbanBoard,
14450
12311
  indexInjectableItems,
14451
12312
  inferContextProfile,
@@ -14469,7 +12330,6 @@ function registerCommanderCommands(program) {
14469
12330
  parseKanbanMarkdown,
14470
12331
  parseSessionFile,
14471
12332
  parseSessionSourceLabel,
14472
- pushFileToRemote,
14473
12333
  qmdEmbed,
14474
12334
  qmdUpdate,
14475
12335
  queryTransitions,
@@ -14489,11 +12349,6 @@ function registerCommanderCommands(program) {
14489
12349
  registerReflectCommand,
14490
12350
  registerReplayCommand,
14491
12351
  registerReweaveCommand,
14492
- registerTailscaleCommands,
14493
- registerTailscaleDiscoverCommand,
14494
- registerTailscaleServeCommand,
14495
- registerTailscaleStatusCommand,
14496
- registerTailscaleSyncCommand,
14497
12352
  removeRouteRule,
14498
12353
  renderTemplate,
14499
12354
  replayCommand,
@@ -14501,25 +12356,17 @@ function registerCommanderCommands(program) {
14501
12356
  resetConfig,
14502
12357
  resolveContextProfile,
14503
12358
  resolveLlmProvider,
14504
- resolvePeerIP,
14505
12359
  resolveVaultPath,
14506
12360
  reweave,
14507
12361
  reweaveCommand,
14508
12362
  runPromptInjection,
14509
12363
  runReflection,
14510
12364
  sentenceChunk,
14511
- serveVault,
14512
12365
  sessionRecapCommand,
14513
12366
  setConfigValue,
14514
12367
  setupCommand,
14515
- stopTailscaleServe,
14516
12368
  stripSupersededObservations,
14517
12369
  syncKanbanBoard,
14518
- syncWithPeer,
14519
- tailscaleDiscoverCommand,
14520
- tailscaleServeCommand,
14521
- tailscaleStatusCommand,
14522
- tailscaleSyncCommand,
14523
12370
  testRouteRule,
14524
12371
  updateProject,
14525
12372
  updateTask