clawvault 2.6.1 → 3.0.0-beta.2

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 (232) hide show
  1. package/bin/command-registration.test.js +1 -3
  2. package/bin/register-core-commands.js +30 -23
  3. package/bin/register-maintenance-commands.js +3 -20
  4. package/bin/register-query-commands.js +23 -0
  5. package/bin/register-task-commands.js +1 -18
  6. package/bin/register-task-commands.test.js +0 -16
  7. package/bin/register-vault-operations-commands.js +1 -29
  8. package/dist/chunk-2TM7DLOL.js +895 -0
  9. package/dist/{chunk-QVMXF7FY.js → chunk-3D6BCTP6.js} +39 -1
  10. package/dist/{chunk-R2MIW5G7.js → chunk-3DHXQHYG.js} +1 -1
  11. package/dist/{chunk-Q2J5YTUF.js → chunk-3NSBOUT3.js} +73 -36
  12. package/dist/chunk-3RG5ZIWI.js +10 -0
  13. package/dist/{chunk-AZYOKJYC.js → chunk-62YTUT6J.js} +2 -2
  14. package/dist/chunk-6U6MK36V.js +205 -0
  15. package/dist/{chunk-4QYGFWRM.js → chunk-7R7O6STJ.js} +4 -4
  16. package/dist/{chunk-VXEOHTSL.js → chunk-C7OK5WKP.js} +4 -4
  17. package/dist/chunk-CMB7UL7C.js +327 -0
  18. package/dist/{chunk-HIHOUSXS.js → chunk-E7MFQB6D.js} +59 -18
  19. package/dist/{chunk-ME37YNW3.js → chunk-F2JEUD4J.js} +6 -4
  20. package/dist/chunk-GAJV4IGR.js +82 -0
  21. package/dist/chunk-GQSLDZTS.js +560 -0
  22. package/dist/{chunk-4OXMU5S2.js → chunk-GUKMRGM7.js} +1 -1
  23. package/dist/{chunk-T76H47ZS.js → chunk-H34S76MB.js} +6 -6
  24. package/dist/{chunk-R6SXNSFD.js → chunk-JY6FYXIT.js} +10 -5
  25. package/dist/chunk-K234IDRJ.js +1073 -0
  26. package/dist/{chunk-IEVLHNLU.js → chunk-LNJA2UGL.js} +86 -9
  27. package/dist/{chunk-MFAWT5O5.js → chunk-LYHGEHXG.js} +1 -0
  28. package/dist/chunk-MFM6K7PU.js +374 -0
  29. package/dist/{chunk-QWQ3TIKS.js → chunk-N2AXRYLC.js} +1 -1
  30. package/dist/chunk-PAH27GSN.js +108 -0
  31. package/dist/{chunk-OIWVQYQF.js → chunk-QBLMXKF2.js} +1 -1
  32. package/dist/{chunk-FHFUXL6G.js → chunk-QK3UCXWL.js} +2 -2
  33. package/dist/{chunk-3BTHWPMB.js → chunk-SJSFRIYS.js} +1 -1
  34. package/dist/{chunk-4VRIMU4O.js → chunk-U55BGUAU.js} +2 -2
  35. package/dist/{chunk-PBEE567J.js → chunk-VGLOTGAS.js} +1 -1
  36. package/dist/{chunk-F55HGNU4.js → chunk-WAZ3NLWL.js} +47 -0
  37. package/dist/{chunk-KL4NAOMO.js → chunk-WGRQ6HDV.js} +1 -1
  38. package/dist/{chunk-UEOUADMO.js → chunk-YKTA5JOJ.js} +13 -10
  39. package/dist/{chunk-XAVB4GB4.js → chunk-ZVVFWOLW.js} +4 -4
  40. package/dist/cli/index.cjs +10033 -0
  41. package/dist/cli/index.d.cts +5 -0
  42. package/dist/cli/index.js +20 -18
  43. package/dist/commands/archive.cjs +287 -0
  44. package/dist/commands/archive.d.cts +11 -0
  45. package/dist/commands/archive.js +1 -0
  46. package/dist/commands/backlog.cjs +721 -0
  47. package/dist/commands/backlog.d.cts +53 -0
  48. package/dist/commands/backlog.js +3 -2
  49. package/dist/commands/blocked.cjs +204 -0
  50. package/dist/commands/blocked.d.cts +26 -0
  51. package/dist/commands/blocked.js +3 -2
  52. package/dist/commands/checkpoint.cjs +244 -0
  53. package/dist/commands/checkpoint.d.cts +41 -0
  54. package/dist/commands/checkpoint.js +2 -1
  55. package/dist/commands/compat.cjs +369 -0
  56. package/dist/commands/compat.d.cts +28 -0
  57. package/dist/commands/compat.js +2 -1
  58. package/dist/commands/context.cjs +2989 -0
  59. package/dist/commands/context.d.cts +2 -0
  60. package/dist/commands/context.js +5 -4
  61. package/dist/commands/doctor.cjs +3062 -0
  62. package/dist/commands/doctor.d.cts +21 -0
  63. package/dist/commands/doctor.d.ts +6 -1
  64. package/dist/commands/doctor.js +13 -11
  65. package/dist/commands/embed.cjs +232 -0
  66. package/dist/commands/embed.d.cts +17 -0
  67. package/dist/commands/embed.js +5 -2
  68. package/dist/commands/entities.cjs +141 -0
  69. package/dist/commands/entities.d.cts +7 -0
  70. package/dist/commands/entities.js +1 -0
  71. package/dist/commands/graph.cjs +501 -0
  72. package/dist/commands/graph.d.cts +21 -0
  73. package/dist/commands/graph.js +1 -0
  74. package/dist/commands/inject.cjs +1636 -0
  75. package/dist/commands/inject.d.cts +2 -0
  76. package/dist/commands/inject.d.ts +1 -1
  77. package/dist/commands/inject.js +4 -2
  78. package/dist/commands/kanban.cjs +884 -0
  79. package/dist/commands/kanban.d.cts +63 -0
  80. package/dist/commands/kanban.js +4 -3
  81. package/dist/commands/link.cjs +965 -0
  82. package/dist/commands/link.d.cts +11 -0
  83. package/dist/commands/link.js +1 -0
  84. package/dist/commands/migrate-observations.cjs +362 -0
  85. package/dist/commands/migrate-observations.d.cts +19 -0
  86. package/dist/commands/migrate-observations.js +3 -2
  87. package/dist/commands/observe.cjs +4099 -0
  88. package/dist/commands/observe.d.cts +23 -0
  89. package/dist/commands/observe.d.ts +1 -0
  90. package/dist/commands/observe.js +11 -9
  91. package/dist/commands/project.cjs +1341 -0
  92. package/dist/commands/project.d.cts +85 -0
  93. package/dist/commands/project.js +5 -4
  94. package/dist/commands/rebuild.cjs +3136 -0
  95. package/dist/commands/rebuild.d.cts +11 -0
  96. package/dist/commands/rebuild.js +10 -8
  97. package/dist/commands/recover.cjs +361 -0
  98. package/dist/commands/recover.d.cts +38 -0
  99. package/dist/commands/recover.js +3 -2
  100. package/dist/commands/reflect.cjs +1008 -0
  101. package/dist/commands/reflect.d.cts +11 -0
  102. package/dist/commands/reflect.js +6 -4
  103. package/dist/commands/repair-session.cjs +457 -0
  104. package/dist/commands/repair-session.d.cts +38 -0
  105. package/dist/commands/repair-session.js +1 -0
  106. package/dist/commands/replay.cjs +4103 -0
  107. package/dist/commands/replay.d.cts +16 -0
  108. package/dist/commands/replay.js +12 -10
  109. package/dist/commands/session-recap.cjs +353 -0
  110. package/dist/commands/session-recap.d.cts +27 -0
  111. package/dist/commands/session-recap.js +1 -0
  112. package/dist/commands/setup.cjs +1302 -0
  113. package/dist/commands/setup.d.cts +100 -0
  114. package/dist/commands/setup.d.ts +90 -2
  115. package/dist/commands/setup.js +21 -2
  116. package/dist/commands/shell-init.cjs +75 -0
  117. package/dist/commands/shell-init.d.cts +7 -0
  118. package/dist/commands/shell-init.js +2 -0
  119. package/dist/commands/sleep.cjs +6028 -0
  120. package/dist/commands/sleep.d.cts +36 -0
  121. package/dist/commands/sleep.d.ts +1 -1
  122. package/dist/commands/sleep.js +17 -15
  123. package/dist/commands/status.cjs +2736 -0
  124. package/dist/commands/status.d.cts +52 -0
  125. package/dist/commands/status.js +12 -10
  126. package/dist/commands/tailscale.cjs +1532 -0
  127. package/dist/commands/tailscale.d.cts +52 -0
  128. package/dist/commands/tailscale.js +1 -0
  129. package/dist/commands/task.cjs +1236 -0
  130. package/dist/commands/task.d.cts +97 -0
  131. package/dist/commands/task.js +4 -3
  132. package/dist/commands/template.cjs +457 -0
  133. package/dist/commands/template.d.cts +36 -0
  134. package/dist/commands/template.js +2 -1
  135. package/dist/commands/wake.cjs +2626 -0
  136. package/dist/commands/wake.d.cts +22 -0
  137. package/dist/commands/wake.d.ts +1 -1
  138. package/dist/commands/wake.js +10 -9
  139. package/dist/context-BUGaWpyL.d.cts +46 -0
  140. package/dist/index.cjs +14001 -0
  141. package/dist/index.d.cts +858 -0
  142. package/dist/index.d.ts +192 -7
  143. package/dist/index.js +101 -75
  144. package/dist/{inject-x65KXWPk.d.ts → inject-Bzi5E-By.d.cts} +1 -1
  145. package/dist/inject-Bzi5E-By.d.ts +137 -0
  146. package/dist/lib/auto-linker.cjs +176 -0
  147. package/dist/lib/auto-linker.d.cts +26 -0
  148. package/dist/lib/auto-linker.js +1 -0
  149. package/dist/lib/canvas-layout.cjs +136 -0
  150. package/dist/lib/canvas-layout.d.cts +31 -0
  151. package/dist/lib/canvas-layout.d.ts +16 -100
  152. package/dist/lib/canvas-layout.js +78 -20
  153. package/dist/lib/config.cjs +78 -0
  154. package/dist/lib/config.d.cts +11 -0
  155. package/dist/lib/config.js +1 -0
  156. package/dist/lib/entity-index.cjs +84 -0
  157. package/dist/lib/entity-index.d.cts +26 -0
  158. package/dist/lib/entity-index.js +1 -0
  159. package/dist/lib/project-utils.cjs +864 -0
  160. package/dist/lib/project-utils.d.cts +97 -0
  161. package/dist/lib/project-utils.js +4 -3
  162. package/dist/lib/session-repair.cjs +239 -0
  163. package/dist/lib/session-repair.d.cts +110 -0
  164. package/dist/lib/session-repair.js +1 -0
  165. package/dist/lib/session-utils.cjs +209 -0
  166. package/dist/lib/session-utils.d.cts +63 -0
  167. package/dist/lib/session-utils.js +1 -0
  168. package/dist/lib/tailscale.cjs +1183 -0
  169. package/dist/lib/tailscale.d.cts +225 -0
  170. package/dist/lib/tailscale.js +1 -0
  171. package/dist/lib/task-utils.cjs +1137 -0
  172. package/dist/lib/task-utils.d.cts +208 -0
  173. package/dist/lib/task-utils.js +3 -2
  174. package/dist/lib/template-engine.cjs +47 -0
  175. package/dist/lib/template-engine.d.cts +11 -0
  176. package/dist/lib/template-engine.js +1 -0
  177. package/dist/lib/webdav.cjs +568 -0
  178. package/dist/lib/webdav.d.cts +109 -0
  179. package/dist/lib/webdav.js +1 -0
  180. package/dist/plugin/index.cjs +1907 -0
  181. package/dist/plugin/index.d.cts +36 -0
  182. package/dist/plugin/index.d.ts +36 -0
  183. package/dist/plugin/index.js +572 -0
  184. package/dist/plugin/inject.cjs +356 -0
  185. package/dist/plugin/inject.d.cts +54 -0
  186. package/dist/plugin/inject.d.ts +54 -0
  187. package/dist/plugin/inject.js +17 -0
  188. package/dist/plugin/observe.cjs +631 -0
  189. package/dist/plugin/observe.d.cts +39 -0
  190. package/dist/plugin/observe.d.ts +39 -0
  191. package/dist/plugin/observe.js +18 -0
  192. package/dist/plugin/templates.cjs +593 -0
  193. package/dist/plugin/templates.d.cts +52 -0
  194. package/dist/plugin/templates.d.ts +52 -0
  195. package/dist/plugin/templates.js +25 -0
  196. package/dist/plugin/types.cjs +18 -0
  197. package/dist/plugin/types.d.cts +209 -0
  198. package/dist/plugin/types.d.ts +209 -0
  199. package/dist/plugin/types.js +0 -0
  200. package/dist/plugin/vault.cjs +927 -0
  201. package/dist/plugin/vault.d.cts +68 -0
  202. package/dist/plugin/vault.d.ts +68 -0
  203. package/dist/plugin/vault.js +22 -0
  204. package/dist/{types-C74wgGL1.d.ts → types-Y2_Um2Ls.d.cts} +44 -1
  205. package/dist/types-Y2_Um2Ls.d.ts +205 -0
  206. package/hooks/clawvault/handler.js +70 -7
  207. package/hooks/clawvault/handler.test.js +91 -0
  208. package/openclaw.plugin.json +56 -0
  209. package/package.json +16 -6
  210. package/templates/memory-event.md +67 -0
  211. package/templates/party.md +63 -0
  212. package/templates/primitive-registry.yaml +551 -0
  213. package/templates/run.md +68 -0
  214. package/templates/trigger.md +68 -0
  215. package/templates/workspace.md +50 -0
  216. package/dashboard/lib/graph-diff.js +0 -104
  217. package/dashboard/lib/graph-diff.test.js +0 -75
  218. package/dashboard/lib/vault-parser.js +0 -556
  219. package/dashboard/lib/vault-parser.test.js +0 -254
  220. package/dashboard/public/app.js +0 -796
  221. package/dashboard/public/index.html +0 -52
  222. package/dashboard/public/styles.css +0 -221
  223. package/dashboard/server.js +0 -374
  224. package/dist/chunk-MAKNAHAW.js +0 -375
  225. package/dist/chunk-MDIH26GC.js +0 -183
  226. package/dist/chunk-MGDEINGP.js +0 -99
  227. package/dist/chunk-RVYA52PY.js +0 -363
  228. package/dist/chunk-TLGBDTYT.js +0 -33
  229. package/dist/commands/canvas.d.ts +0 -15
  230. package/dist/commands/canvas.js +0 -199
  231. package/dist/commands/sync-bd.d.ts +0 -10
  232. package/dist/commands/sync-bd.js +0 -9
@@ -0,0 +1,327 @@
1
+ // src/plugin/inject.ts
2
+ import { existsSync, readdirSync, readFileSync, statSync } from "fs";
3
+ import { join, relative } from "path";
4
+ function parseYamlFrontmatter(content) {
5
+ const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
6
+ if (!match) return null;
7
+ const yamlContent = match[1];
8
+ const body = match[2];
9
+ try {
10
+ const frontmatter = parseSimpleYaml(yamlContent);
11
+ return { frontmatter, body };
12
+ } catch {
13
+ return null;
14
+ }
15
+ }
16
+ function parseSimpleYaml(yaml) {
17
+ const result = {};
18
+ const lines = yaml.split("\n");
19
+ for (const line of lines) {
20
+ if (!line.trim() || line.trim().startsWith("#")) continue;
21
+ const colonIndex = line.indexOf(":");
22
+ if (colonIndex === -1) continue;
23
+ const key = line.slice(0, colonIndex).trim();
24
+ const valueStr = line.slice(colonIndex + 1).trim();
25
+ if (valueStr === "" || valueStr.startsWith("|") || valueStr.startsWith(">")) continue;
26
+ result[key] = parseYamlValue(valueStr);
27
+ }
28
+ return result;
29
+ }
30
+ function parseYamlValue(value) {
31
+ if (value === "" || value === "null" || value === "~") return null;
32
+ if (value === "true") return true;
33
+ if (value === "false") return false;
34
+ if (/^-?\d+$/.test(value)) return parseInt(value, 10);
35
+ if (/^-?\d+\.\d+$/.test(value)) return parseFloat(value);
36
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
37
+ return value.slice(1, -1);
38
+ }
39
+ return value;
40
+ }
41
+ function scanVaultFiles(vaultPath, options = {}) {
42
+ const files = [];
43
+ const maxAge = options.maxAge ?? 7 * 24 * 60 * 60 * 1e3;
44
+ const limit = options.limit ?? 100;
45
+ const now = Date.now();
46
+ const cutoff = now - maxAge;
47
+ const dirsToScan = findVaultDirectories(vaultPath);
48
+ for (const dir of dirsToScan) {
49
+ if (!existsSync(dir)) continue;
50
+ try {
51
+ scanDirectory(dir, vaultPath, files, cutoff, options.primitiveTypes);
52
+ } catch {
53
+ }
54
+ }
55
+ files.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
56
+ return files.slice(0, limit);
57
+ }
58
+ function findVaultDirectories(vaultPath) {
59
+ const dirs = [];
60
+ dirs.push(vaultPath);
61
+ const commonDirs = [
62
+ "tasks",
63
+ "projects",
64
+ "decisions",
65
+ "people",
66
+ "persons",
67
+ "notes",
68
+ "daily",
69
+ "journal",
70
+ "ledger",
71
+ "memory",
72
+ "memories",
73
+ "observations",
74
+ "lessons",
75
+ "triggers",
76
+ "runs",
77
+ "checkpoints",
78
+ "handoffs",
79
+ "workspaces",
80
+ "parties"
81
+ ];
82
+ for (const subdir of commonDirs) {
83
+ const fullPath = join(vaultPath, subdir);
84
+ if (existsSync(fullPath)) {
85
+ dirs.push(fullPath);
86
+ }
87
+ }
88
+ try {
89
+ const entries = readdirSync(vaultPath, { withFileTypes: true });
90
+ for (const entry of entries) {
91
+ if (entry.isDirectory() && !entry.name.startsWith(".") && !entry.name.startsWith("_")) {
92
+ const fullPath = join(vaultPath, entry.name);
93
+ if (!dirs.includes(fullPath)) {
94
+ dirs.push(fullPath);
95
+ }
96
+ }
97
+ }
98
+ } catch {
99
+ }
100
+ return dirs;
101
+ }
102
+ function scanDirectory(dir, vaultPath, files, cutoff, primitiveTypes) {
103
+ const entries = readdirSync(dir, { withFileTypes: true });
104
+ for (const entry of entries) {
105
+ if (entry.name.startsWith(".") || entry.name.startsWith("_")) continue;
106
+ const fullPath = join(dir, entry.name);
107
+ if (entry.isDirectory()) {
108
+ const depth = fullPath.replace(vaultPath, "").split("/").length;
109
+ if (depth <= 3) {
110
+ scanDirectory(fullPath, vaultPath, files, cutoff, primitiveTypes);
111
+ }
112
+ } else if (entry.name.endsWith(".md")) {
113
+ try {
114
+ const stat = statSync(fullPath);
115
+ if (stat.mtimeMs < cutoff) continue;
116
+ const content = readFileSync(fullPath, "utf-8");
117
+ const parsed = parseYamlFrontmatter(content);
118
+ if (!parsed) continue;
119
+ const primitiveType = detectPrimitiveType(parsed.frontmatter, fullPath);
120
+ if (primitiveTypes && !primitiveTypes.includes(primitiveType)) continue;
121
+ files.push({
122
+ path: fullPath,
123
+ relativePath: relative(vaultPath, fullPath),
124
+ primitiveType,
125
+ frontmatter: parsed.frontmatter,
126
+ content: parsed.body,
127
+ modifiedAt: stat.mtime,
128
+ createdAt: stat.birthtime
129
+ });
130
+ } catch {
131
+ }
132
+ }
133
+ }
134
+ }
135
+ function detectPrimitiveType(frontmatter, filePath) {
136
+ if (frontmatter.primitive) return String(frontmatter.primitive);
137
+ if (frontmatter.type) return String(frontmatter.type);
138
+ const pathLower = filePath.toLowerCase();
139
+ if (pathLower.includes("/tasks/")) return "task";
140
+ if (pathLower.includes("/projects/")) return "project";
141
+ if (pathLower.includes("/decisions/")) return "decision";
142
+ if (pathLower.includes("/people/") || pathLower.includes("/persons/")) return "person";
143
+ if (pathLower.includes("/daily/") || pathLower.includes("/journal/")) return "daily-note";
144
+ if (pathLower.includes("/lessons/")) return "lesson";
145
+ if (pathLower.includes("/triggers/")) return "trigger";
146
+ if (pathLower.includes("/runs/")) return "run";
147
+ if (pathLower.includes("/checkpoints/")) return "checkpoint";
148
+ if (pathLower.includes("/handoffs/")) return "handoff";
149
+ if (pathLower.includes("/ledger/")) return "memory_event";
150
+ if (pathLower.includes("/memory/") || pathLower.includes("/memories/")) return "memory_event";
151
+ return "unknown";
152
+ }
153
+ function buildSessionRecap(vaultPath, options = {}) {
154
+ const maxAge = options.maxAge ?? 24 * 60 * 60 * 1e3;
155
+ const limit = options.limit ?? 20;
156
+ const includeContent = options.includeContent ?? false;
157
+ const files = scanVaultFiles(vaultPath, { maxAge, limit });
158
+ if (files.length === 0) {
159
+ return {
160
+ xml: "",
161
+ fileCount: 0,
162
+ primitiveGroups: {},
163
+ timeRange: null
164
+ };
165
+ }
166
+ const groups = {};
167
+ for (const file of files) {
168
+ const type = file.primitiveType;
169
+ if (!groups[type]) groups[type] = [];
170
+ groups[type].push(file);
171
+ }
172
+ const lines = ["<session-recap>"];
173
+ lines.push(`<summary>Found ${files.length} recent items across ${Object.keys(groups).length} categories</summary>`);
174
+ for (const [primitiveType, groupFiles] of Object.entries(groups)) {
175
+ lines.push(`<${primitiveType}-items count="${groupFiles.length}">`);
176
+ for (const file of groupFiles.slice(0, 5)) {
177
+ const title = file.frontmatter.title || file.frontmatter.summary || file.relativePath;
178
+ const status = file.frontmatter.status || "";
179
+ const modified = file.modifiedAt.toISOString().slice(0, 16).replace("T", " ");
180
+ lines.push(` <item path="${file.relativePath}" modified="${modified}"${status ? ` status="${status}"` : ""}>`);
181
+ lines.push(` <title>${escapeXml(String(title))}</title>`);
182
+ if (includeContent && file.content) {
183
+ const snippet = file.content.slice(0, 200).replace(/\n/g, " ").trim();
184
+ if (snippet) {
185
+ lines.push(` <snippet>${escapeXml(snippet)}</snippet>`);
186
+ }
187
+ }
188
+ lines.push(" </item>");
189
+ }
190
+ if (groupFiles.length > 5) {
191
+ lines.push(` <more count="${groupFiles.length - 5}" />`);
192
+ }
193
+ lines.push(`</${primitiveType}-items>`);
194
+ }
195
+ lines.push("</session-recap>");
196
+ const sortedByTime = [...files].sort((a, b) => a.modifiedAt.getTime() - b.modifiedAt.getTime());
197
+ const timeRange = {
198
+ oldest: sortedByTime[0].modifiedAt,
199
+ newest: sortedByTime[sortedByTime.length - 1].modifiedAt
200
+ };
201
+ const primitiveGroups = {};
202
+ for (const [type, groupFiles] of Object.entries(groups)) {
203
+ primitiveGroups[type] = groupFiles.length;
204
+ }
205
+ return {
206
+ xml: lines.join("\n"),
207
+ fileCount: files.length,
208
+ primitiveGroups,
209
+ timeRange
210
+ };
211
+ }
212
+ function buildPreferenceContext(vaultPath, options = {}) {
213
+ const maxAge = options.maxAge ?? 30 * 24 * 60 * 60 * 1e3;
214
+ const limit = options.limit ?? 50;
215
+ const files = scanVaultFiles(vaultPath, { maxAge, limit: limit * 2 });
216
+ const preferenceFiles = files.filter((file) => {
217
+ if (file.frontmatter.type === "preference") return true;
218
+ if (file.primitiveType === "memory_event" && file.frontmatter.type === "preference") return true;
219
+ const content = (file.content || "").toLowerCase();
220
+ if (/\b(prefer|like|love|hate|dislike|want|need|always|never)\b/.test(content)) {
221
+ return true;
222
+ }
223
+ return false;
224
+ }).slice(0, limit);
225
+ if (preferenceFiles.length === 0) {
226
+ return {
227
+ xml: "",
228
+ preferenceCount: 0,
229
+ categories: []
230
+ };
231
+ }
232
+ const categories = /* @__PURE__ */ new Set();
233
+ for (const file of preferenceFiles) {
234
+ if (file.frontmatter.category) {
235
+ categories.add(String(file.frontmatter.category));
236
+ }
237
+ }
238
+ const lines = ["<user-preferences>"];
239
+ for (const file of preferenceFiles) {
240
+ const summary = file.frontmatter.summary || file.frontmatter.title || extractPreferenceSummary(file.content);
241
+ if (!summary) continue;
242
+ const category = file.frontmatter.category || "general";
243
+ const sentiment = file.frontmatter.sentiment || inferSentiment(file.content);
244
+ lines.push(` <preference category="${escapeXml(String(category))}" sentiment="${sentiment}">`);
245
+ lines.push(` ${escapeXml(String(summary))}`);
246
+ lines.push(" </preference>");
247
+ }
248
+ lines.push("</user-preferences>");
249
+ return {
250
+ xml: lines.join("\n"),
251
+ preferenceCount: preferenceFiles.length,
252
+ categories: Array.from(categories)
253
+ };
254
+ }
255
+ function extractPreferenceSummary(content) {
256
+ if (!content) return "";
257
+ const sentences = content.split(/[.!?\n]+/).map((s) => s.trim()).filter((s) => s.length > 10);
258
+ for (const sentence of sentences) {
259
+ if (/\b(prefer|like|love|hate|dislike|want|need|always|never)\b/i.test(sentence)) {
260
+ return sentence.slice(0, 150);
261
+ }
262
+ }
263
+ return sentences[0]?.slice(0, 150) || "";
264
+ }
265
+ function inferSentiment(content) {
266
+ if (!content) return "neutral";
267
+ const lower = content.toLowerCase();
268
+ if (/\b(love|like|prefer|enjoy|want|need|always)\b/.test(lower)) return "positive";
269
+ if (/\b(hate|dislike|don't like|never|avoid)\b/.test(lower)) return "negative";
270
+ return "neutral";
271
+ }
272
+ function formatMemoriesForContext(results, collection) {
273
+ if (results.length === 0) return "";
274
+ const lines = results.map((r, i) => {
275
+ const file = (r.file || "").replace(`qmd://${collection}/`, "");
276
+ const snippet = (r.snippet || "").replace(/@@ .+? @@\s*\(.+?\)\n?/g, "").trim() || r.title || "";
277
+ return `${i + 1}. [${file}] ${snippet}`;
278
+ });
279
+ return `<relevant-memories>
280
+ These are recalled from long-term vault memory. Treat as historical context.
281
+ ${lines.join("\n")}
282
+ </relevant-memories>`;
283
+ }
284
+ function formatSearchResults(results, collection) {
285
+ if (results.length === 0) return "No relevant memories found.";
286
+ return results.map((r, i) => {
287
+ const file = (r.file || "").replace(`qmd://${collection}/`, "");
288
+ const snippet = (r.snippet || "").replace(/@@ .+? @@\s*\(.+?\)\n?/g, "").trim() || r.title || "(no content)";
289
+ const score = ((r.score ?? 0) * 100).toFixed(0);
290
+ return `${i + 1}. [${file}] ${snippet} (${score}%)`;
291
+ }).join("\n");
292
+ }
293
+ function escapeXml(str) {
294
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
295
+ }
296
+ function buildFullContext(vaultPath, options = {}) {
297
+ const parts = [];
298
+ if (options.includeRecap !== false) {
299
+ const recap = buildSessionRecap(vaultPath, {
300
+ maxAge: options.recapMaxAge ?? 24 * 60 * 60 * 1e3,
301
+ limit: 15,
302
+ includeContent: true
303
+ });
304
+ if (recap.xml) {
305
+ parts.push(recap.xml);
306
+ }
307
+ }
308
+ if (options.includePreferences !== false) {
309
+ const prefs = buildPreferenceContext(vaultPath, {
310
+ maxAge: options.preferenceMaxAge ?? 30 * 24 * 60 * 60 * 1e3,
311
+ limit: 20
312
+ });
313
+ if (prefs.xml) {
314
+ parts.push(prefs.xml);
315
+ }
316
+ }
317
+ return parts.join("\n\n");
318
+ }
319
+
320
+ export {
321
+ scanVaultFiles,
322
+ buildSessionRecap,
323
+ buildPreferenceContext,
324
+ formatMemoriesForContext,
325
+ formatSearchResults,
326
+ buildFullContext
327
+ };
@@ -1,14 +1,36 @@
1
+ import {
2
+ resolveClaudeOAuthToken
3
+ } from "./chunk-PAH27GSN.js";
4
+
1
5
  // src/lib/llm-provider.ts
2
6
  var DEFAULT_MODELS = {
3
- anthropic: "claude-3-5-haiku-latest",
7
+ anthropic: "claude-haiku-4-5",
4
8
  openai: "gpt-4o-mini",
5
9
  gemini: "gemini-2.0-flash"
6
10
  };
7
- function resolveLlmProvider() {
11
+ async function resolveAnthropicAuth(fetchImpl) {
12
+ const oauthEnvToken = process.env.ANTHROPIC_OAUTH_TOKEN?.trim();
13
+ if (oauthEnvToken) {
14
+ return { token: oauthEnvToken, isOAuth: true };
15
+ }
16
+ const apiKey = process.env.ANTHROPIC_API_KEY?.trim();
17
+ if (apiKey) {
18
+ return { token: apiKey, isOAuth: false };
19
+ }
20
+ if (process.env.CLAWVAULT_CLAUDE_AUTH) {
21
+ const oauthToken = await resolveClaudeOAuthToken({ fetchImpl });
22
+ if (oauthToken) {
23
+ return { token: oauthToken, isOAuth: true };
24
+ }
25
+ }
26
+ return null;
27
+ }
28
+ async function resolveLlmProvider(fetchImpl) {
8
29
  if (process.env.CLAWVAULT_NO_LLM) {
9
30
  return null;
10
31
  }
11
- if (process.env.ANTHROPIC_API_KEY) {
32
+ const anthropicAuth = await resolveAnthropicAuth(fetchImpl);
33
+ if (anthropicAuth) {
12
34
  return "anthropic";
13
35
  }
14
36
  if (process.env.OPENAI_API_KEY) {
@@ -20,7 +42,7 @@ function resolveLlmProvider() {
20
42
  return null;
21
43
  }
22
44
  async function requestLlmCompletion(options) {
23
- const provider = options.provider ?? resolveLlmProvider();
45
+ const provider = options.provider ?? await resolveLlmProvider(options.fetchImpl);
24
46
  if (!provider) {
25
47
  return "";
26
48
  }
@@ -33,24 +55,43 @@ async function requestLlmCompletion(options) {
33
55
  return callOpenAI(options, provider);
34
56
  }
35
57
  async function callAnthropic(options, provider) {
36
- const apiKey = process.env.ANTHROPIC_API_KEY;
37
- if (!apiKey) {
58
+ const fetchImpl = options.fetchImpl ?? fetch;
59
+ const auth = await resolveAnthropicAuth(fetchImpl);
60
+ if (!auth) {
38
61
  return "";
39
62
  }
40
- const fetchImpl = options.fetchImpl ?? fetch;
63
+ const headers = auth.isOAuth ? {
64
+ "content-type": "application/json",
65
+ "authorization": `Bearer ${auth.token}`,
66
+ "anthropic-version": "2023-06-01",
67
+ "anthropic-beta": "claude-code-20250219,oauth-2025-04-20",
68
+ "x-app": "cli",
69
+ "user-agent": "claude-cli/1.0.0 (external, cli)"
70
+ } : {
71
+ "content-type": "application/json",
72
+ "x-api-key": auth.token,
73
+ "anthropic-version": "2023-06-01"
74
+ };
75
+ const systemMessages = [];
76
+ if (auth.isOAuth) {
77
+ systemMessages.push({ type: "text", text: "You are Claude Code, Anthropic's official CLI for Claude." });
78
+ }
79
+ if (options.systemPrompt?.trim()) {
80
+ systemMessages.push({ type: "text", text: options.systemPrompt.trim() });
81
+ }
82
+ const body = {
83
+ model: options.model ?? DEFAULT_MODELS[provider],
84
+ temperature: options.temperature ?? 0.1,
85
+ max_tokens: options.maxTokens ?? 1200,
86
+ messages: [{ role: "user", content: options.prompt }]
87
+ };
88
+ if (systemMessages.length > 0) {
89
+ body.system = systemMessages;
90
+ }
41
91
  const response = await fetchImpl("https://api.anthropic.com/v1/messages", {
42
92
  method: "POST",
43
- headers: {
44
- "content-type": "application/json",
45
- "x-api-key": apiKey,
46
- "anthropic-version": "2023-06-01"
47
- },
48
- body: JSON.stringify({
49
- model: options.model ?? DEFAULT_MODELS[provider],
50
- temperature: options.temperature ?? 0.1,
51
- max_tokens: options.maxTokens ?? 1200,
52
- messages: [{ role: "user", content: options.prompt }]
53
- })
93
+ headers,
94
+ body: JSON.stringify(body)
54
95
  });
55
96
  if (!response.ok) {
56
97
  throw new Error(`Anthropic request failed (${response.status})`);
@@ -3,10 +3,10 @@ import {
3
3
  } from "./chunk-P5EPF6MB.js";
4
4
  import {
5
5
  observeActiveSessions
6
- } from "./chunk-IEVLHNLU.js";
6
+ } from "./chunk-LNJA2UGL.js";
7
7
  import {
8
8
  Observer
9
- } from "./chunk-Q2J5YTUF.js";
9
+ } from "./chunk-3NSBOUT3.js";
10
10
  import {
11
11
  resolveVaultPath
12
12
  } from "./chunk-MXSSG3QU.js";
@@ -291,7 +291,8 @@ async function observeCommand(options) {
291
291
  threshold: options.threshold,
292
292
  reflectThreshold: options.reflectThreshold,
293
293
  model: options.model,
294
- extractTasks: options.extractTasks
294
+ extractTasks: options.extractTasks,
295
+ maxSessions: options.maxSessions
295
296
  });
296
297
  const failedSessionCount = result.failedSessionCount ?? 0;
297
298
  if (options.cron) {
@@ -379,7 +380,7 @@ async function observeCommand(options) {
379
380
  await watchSessions(observer, watchPath);
380
381
  }
381
382
  function registerObserveCommand(program) {
382
- program.command("observe").description("Observe session files and build observational memory").option("--watch <path>", "Watch session file or directory").option("--active", "Observe active OpenClaw sessions incrementally").option("--cron", "Run one-shot active observation for cron hooks").option("--agent <id>", "OpenClaw agent ID (default: OPENCLAW_AGENT_ID or clawdious)").option("--min-new <bytes>", "Override minimum new-content threshold in bytes").option("--sessions-dir <path>", "Override OpenClaw sessions directory").option("--dry-run", "Show active observation candidates without compressing").option("--threshold <n>", "Compression token threshold", "30000").option("--reflect-threshold <n>", "Reflection token threshold", "40000").option("--model <model>", "LLM model override").option("--extract-tasks", "Extract task-like observations into backlog", true).option("--no-extract-tasks", "Disable task extraction from observations").option("--compress <file>", "One-shot compression for a conversation file").option("--daemon", "Run in detached background mode").option("-v, --vault <path>", "Vault path").action(async (rawOptions) => {
383
+ program.command("observe").description("Observe session files and build observational memory").option("--watch <path>", "Watch session file or directory").option("--active", "Observe active OpenClaw sessions incrementally").option("--cron", "Run one-shot active observation for cron hooks").option("--agent <id>", "OpenClaw agent ID (default: OPENCLAW_AGENT_ID or clawdious)").option("--min-new <bytes>", "Override minimum new-content threshold in bytes").option("--sessions-dir <path>", "Override OpenClaw sessions directory").option("--dry-run", "Show active observation candidates without compressing").option("--max-sessions <n>", "Limit number of sessions to observe (recommended: 10)").option("--threshold <n>", "Compression token threshold", "30000").option("--reflect-threshold <n>", "Reflection token threshold", "40000").option("--model <model>", "LLM model override").option("--extract-tasks", "Extract task-like observations into backlog", true).option("--no-extract-tasks", "Disable task extraction from observations").option("--compress <file>", "One-shot compression for a conversation file").option("--daemon", "Run in detached background mode").option("-v, --vault <path>", "Vault path").action(async (rawOptions) => {
383
384
  await observeCommand({
384
385
  watch: rawOptions.watch,
385
386
  active: rawOptions.active,
@@ -388,6 +389,7 @@ function registerObserveCommand(program) {
388
389
  minNew: rawOptions.minNew ? parsePositiveInteger(rawOptions.minNew, "min-new") : void 0,
389
390
  sessionsDir: rawOptions.sessionsDir,
390
391
  dryRun: rawOptions.dryRun,
392
+ maxSessions: rawOptions.maxSessions ? parsePositiveInteger(rawOptions.maxSessions, "max-sessions") : void 0,
391
393
  threshold: parsePositiveInteger(rawOptions.threshold, "threshold"),
392
394
  reflectThreshold: parsePositiveInteger(rawOptions.reflectThreshold, "reflect-threshold"),
393
395
  model: rawOptions.model,
@@ -0,0 +1,82 @@
1
+ import {
2
+ registerTailscaleCommands
3
+ } from "./chunk-THRJVD4L.js";
4
+ import {
5
+ registerObserveCommand
6
+ } from "./chunk-F2JEUD4J.js";
7
+ import {
8
+ registerReflectCommand
9
+ } from "./chunk-SJSFRIYS.js";
10
+ import {
11
+ registerEmbedCommand
12
+ } from "./chunk-7R7O6STJ.js";
13
+ import {
14
+ registerInjectCommand
15
+ } from "./chunk-U55BGUAU.js";
16
+ import {
17
+ resolveVaultPath
18
+ } from "./chunk-MXSSG3QU.js";
19
+ import {
20
+ registerContextCommand
21
+ } from "./chunk-ZVVFWOLW.js";
22
+ import {
23
+ reweave
24
+ } from "./chunk-K234IDRJ.js";
25
+
26
+ // src/commands/reweave.ts
27
+ async function reweaveCommand(options) {
28
+ const vaultPath = resolveVaultPath({ explicitPath: options.vaultPath });
29
+ const result = reweave({
30
+ vaultPath,
31
+ since: options.since,
32
+ dryRun: options.dryRun,
33
+ similarityThreshold: options.threshold
34
+ });
35
+ console.log(`Reweave: scanned ${result.filesScanned} files, checked ${result.observationsChecked} observations`);
36
+ if (result.supersessions.length === 0) {
37
+ console.log("No knowledge updates detected.");
38
+ return;
39
+ }
40
+ console.log(`
41
+ Found ${result.supersessions.length} supersession(s):`);
42
+ for (const s of result.supersessions) {
43
+ console.log(`
44
+ OLD [${s.oldObservation.date}] ${s.oldObservation.content.slice(0, 80)}`);
45
+ console.log(` NEW [${s.newObservation.date}] ${s.newObservation.content.slice(0, 80)}`);
46
+ console.log(` Reason: ${s.reason}`);
47
+ }
48
+ if (result.dryRun) {
49
+ console.log("\n(dry run \u2014 no files modified)");
50
+ } else {
51
+ console.log(`
52
+ ${result.supersessions.length} observation(s) marked as superseded.`);
53
+ }
54
+ }
55
+ function registerReweaveCommand(program) {
56
+ program.command("reweave").description("Backward memory consolidation \u2014 detect and mark superseded observations").option("--since <date>", "Only check observations since this date (YYYY-MM-DD)").option("--dry-run", "Show what would be superseded without writing").option("--threshold <n>", "Entity similarity threshold (0-1, default 0.3)", "0.3").option("-v, --vault <path>", "Vault path").action(async (rawOptions) => {
57
+ await reweaveCommand({
58
+ vaultPath: rawOptions.vault,
59
+ since: rawOptions.since,
60
+ dryRun: rawOptions.dryRun,
61
+ threshold: parseFloat(rawOptions.threshold)
62
+ });
63
+ });
64
+ }
65
+
66
+ // src/cli/index.ts
67
+ function registerCliCommands(program) {
68
+ registerContextCommand(program);
69
+ registerInjectCommand(program);
70
+ registerObserveCommand(program);
71
+ registerReflectCommand(program);
72
+ registerEmbedCommand(program);
73
+ registerReweaveCommand(program);
74
+ registerTailscaleCommands(program);
75
+ return program;
76
+ }
77
+
78
+ export {
79
+ reweaveCommand,
80
+ registerReweaveCommand,
81
+ registerCliCommands
82
+ };