clawvault 2.6.1 → 2.6.4

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 (125) hide show
  1. package/README.md +352 -20
  2. package/bin/clawvault.js +8 -2
  3. package/bin/command-runtime.js +9 -1
  4. package/bin/register-maintenance-commands.js +19 -0
  5. package/bin/register-query-commands.js +58 -6
  6. package/bin/register-workgraph-commands.js +451 -0
  7. package/dist/{chunk-VXEOHTSL.js → chunk-2JQ3O2YL.js} +1 -1
  8. package/dist/{chunk-VR5NE7PZ.js → chunk-2RAZ4ZFE.js} +1 -1
  9. package/dist/chunk-2ZDO52B4.js +52 -0
  10. package/dist/chunk-4BQTQMJP.js +93 -0
  11. package/dist/{chunk-MAKNAHAW.js → chunk-5PJ4STIC.js} +98 -8
  12. package/dist/{chunk-IEVLHNLU.js → chunk-627Q3QWK.js} +3 -3
  13. package/dist/{chunk-R6SXNSFD.js → chunk-6NYYDNNG.js} +3 -3
  14. package/dist/chunk-ECRZL5XR.js +50 -0
  15. package/dist/chunk-GNJL4YGR.js +79 -0
  16. package/dist/{chunk-OZ7RIXTO.js → chunk-IIOU45CK.js} +1 -1
  17. package/dist/chunk-L4HSSQ6T.js +152 -0
  18. package/dist/{chunk-XAVB4GB4.js → chunk-LIGHWOH6.js} +1 -1
  19. package/dist/{chunk-PBEE567J.js → chunk-LUBZXECN.js} +2 -2
  20. package/dist/{chunk-UEOUADMO.js → chunk-MFL6EEPF.js} +204 -35
  21. package/dist/chunk-MM6QGW3P.js +207 -0
  22. package/dist/{chunk-T76H47ZS.js → chunk-MNPUYCHQ.js} +1 -1
  23. package/dist/{chunk-TLGBDTYT.js → chunk-MPOSMDMU.js} +6 -6
  24. package/dist/{chunk-RVYA52PY.js → chunk-NJYJL5AA.js} +1 -1
  25. package/dist/{chunk-Q2J5YTUF.js → chunk-OQGYFZ4A.js} +669 -33
  26. package/dist/{chunk-ME37YNW3.js → chunk-P7SY3D4E.js} +3 -3
  27. package/dist/chunk-RHISK3SZ.js +189 -0
  28. package/dist/{chunk-3BTHWPMB.js → chunk-S5OJEGFG.js} +2 -2
  29. package/dist/{chunk-MGDEINGP.js → chunk-SS4B7P7V.js} +1 -1
  30. package/dist/chunk-U4O6C46S.js +154 -0
  31. package/dist/{chunk-ITPEXLHA.js → chunk-URXDAUVH.js} +24 -5
  32. package/dist/chunk-WIOLLGAD.js +190 -0
  33. package/dist/chunk-WMGIIABP.js +15 -0
  34. package/dist/{chunk-QVMXF7FY.js → chunk-X3SPPUFG.js} +50 -0
  35. package/dist/{chunk-THRJVD4L.js → chunk-Y6VJKXGL.js} +1 -1
  36. package/dist/{chunk-KL4NAOMO.js → chunk-YDWHS4LJ.js} +49 -9
  37. package/dist/{chunk-4VRIMU4O.js → chunk-YNIPYN4F.js} +4 -4
  38. package/dist/{chunk-HIHOUSXS.js → chunk-YXQCA6B7.js} +105 -1
  39. package/dist/cli/index.js +18 -16
  40. package/dist/commands/archive.js +3 -2
  41. package/dist/commands/backlog.js +1 -0
  42. package/dist/commands/blocked.js +1 -0
  43. package/dist/commands/canvas.js +1 -0
  44. package/dist/commands/checkpoint.js +1 -0
  45. package/dist/commands/compat.js +2 -1
  46. package/dist/commands/context.js +5 -3
  47. package/dist/commands/doctor.d.ts +10 -1
  48. package/dist/commands/doctor.js +11 -8
  49. package/dist/commands/embed.js +5 -3
  50. package/dist/commands/entities.js +2 -1
  51. package/dist/commands/graph.js +3 -2
  52. package/dist/commands/inject.d.ts +1 -1
  53. package/dist/commands/inject.js +4 -3
  54. package/dist/commands/kanban.js +1 -0
  55. package/dist/commands/link.js +2 -1
  56. package/dist/commands/migrate-observations.js +3 -2
  57. package/dist/commands/observe.js +8 -6
  58. package/dist/commands/project.js +1 -0
  59. package/dist/commands/rebuild-embeddings.d.ts +21 -0
  60. package/dist/commands/rebuild-embeddings.js +91 -0
  61. package/dist/commands/rebuild.js +6 -4
  62. package/dist/commands/recover.js +1 -0
  63. package/dist/commands/reflect.js +5 -4
  64. package/dist/commands/repair-session.js +1 -0
  65. package/dist/commands/replay.js +7 -6
  66. package/dist/commands/session-recap.js +1 -0
  67. package/dist/commands/setup.js +3 -2
  68. package/dist/commands/shell-init.js +2 -0
  69. package/dist/commands/sleep.d.ts +1 -1
  70. package/dist/commands/sleep.js +8 -6
  71. package/dist/commands/status.d.ts +2 -0
  72. package/dist/commands/status.js +35 -24
  73. package/dist/commands/sync-bd.js +3 -2
  74. package/dist/commands/tailscale.js +3 -2
  75. package/dist/commands/task.js +1 -0
  76. package/dist/commands/template.js +1 -0
  77. package/dist/commands/wake.d.ts +1 -1
  78. package/dist/commands/wake.js +4 -2
  79. package/dist/index.d.ts +333 -10
  80. package/dist/index.js +320 -33
  81. package/dist/{inject-x65KXWPk.d.ts → inject-DYUrDqQO.d.ts} +2 -2
  82. package/dist/ledger-B7g7jhqG.d.ts +44 -0
  83. package/dist/lib/auto-linker.js +1 -0
  84. package/dist/lib/canvas-layout.js +1 -0
  85. package/dist/lib/config.d.ts +27 -3
  86. package/dist/lib/config.js +4 -1
  87. package/dist/lib/entity-index.js +1 -0
  88. package/dist/lib/project-utils.js +1 -0
  89. package/dist/lib/session-repair.js +1 -0
  90. package/dist/lib/session-utils.js +1 -0
  91. package/dist/lib/tailscale.js +1 -0
  92. package/dist/lib/task-utils.js +1 -0
  93. package/dist/lib/template-engine.js +1 -0
  94. package/dist/lib/webdav.js +1 -0
  95. package/dist/onnxruntime_binding-5QEF3SUC.node +0 -0
  96. package/dist/onnxruntime_binding-BKPKNEGC.node +0 -0
  97. package/dist/onnxruntime_binding-FMOXGIUT.node +0 -0
  98. package/dist/onnxruntime_binding-OI2KMXC5.node +0 -0
  99. package/dist/onnxruntime_binding-UX44MLAZ.node +0 -0
  100. package/dist/onnxruntime_binding-Y2W7N7WY.node +0 -0
  101. package/dist/registry-BR4326o0.d.ts +30 -0
  102. package/dist/store-CA-6sKCJ.d.ts +34 -0
  103. package/dist/thread-B9LhXNU0.d.ts +41 -0
  104. package/dist/transformers.node-A2ZRORSQ.js +46775 -0
  105. package/dist/{types-C74wgGL1.d.ts → types-BbWJoC1c.d.ts} +1 -1
  106. package/dist/workgraph/index.d.ts +5 -0
  107. package/dist/workgraph/index.js +23 -0
  108. package/dist/workgraph/ledger.d.ts +2 -0
  109. package/dist/workgraph/ledger.js +25 -0
  110. package/dist/workgraph/registry.d.ts +2 -0
  111. package/dist/workgraph/registry.js +19 -0
  112. package/dist/workgraph/store.d.ts +2 -0
  113. package/dist/workgraph/store.js +25 -0
  114. package/dist/workgraph/thread.d.ts +2 -0
  115. package/dist/workgraph/thread.js +25 -0
  116. package/dist/workgraph/types.d.ts +54 -0
  117. package/dist/workgraph/types.js +7 -0
  118. package/hooks/clawvault/HOOK.md +34 -4
  119. package/hooks/clawvault/handler.js +751 -8
  120. package/hooks/clawvault/handler.test.js +247 -0
  121. package/hooks/clawvault/openclaw.plugin.json +72 -0
  122. package/openclaw.plugin.json +84 -0
  123. package/package.json +8 -4
  124. package/dist/chunk-4QYGFWRM.js +0 -88
  125. package/dist/chunk-MXSSG3QU.js +0 -42
@@ -6,20 +6,26 @@ import {
6
6
  } from "./chunk-4VQTUVH7.js";
7
7
  import {
8
8
  getObserverStaleness
9
- } from "./chunk-IEVLHNLU.js";
9
+ } from "./chunk-627Q3QWK.js";
10
10
  import {
11
11
  ClawVault,
12
12
  findVault
13
- } from "./chunk-KL4NAOMO.js";
13
+ } from "./chunk-YDWHS4LJ.js";
14
14
  import {
15
+ listQmdCollections,
16
+ loadVaultQmdConfig
17
+ } from "./chunk-WIOLLGAD.js";
18
+ import {
19
+ QMD_INSTALL_COMMAND,
20
+ QMD_INSTALL_URL,
15
21
  hasQmd
16
- } from "./chunk-MAKNAHAW.js";
22
+ } from "./chunk-5PJ4STIC.js";
17
23
  import {
18
24
  loadMemoryGraphIndex
19
25
  } from "./chunk-ZZA73MFY.js";
20
26
  import {
21
27
  checkOpenClawCompatibility
22
- } from "./chunk-QVMXF7FY.js";
28
+ } from "./chunk-X3SPPUFG.js";
23
29
 
24
30
  // src/commands/doctor.ts
25
31
  import * as fs from "fs";
@@ -29,6 +35,12 @@ var CLAWVAULT_DIR = ".clawvault";
29
35
  var CHECKPOINT_FILE = "last-checkpoint.json";
30
36
  var DAY_MS = 24 * 60 * 60 * 1e3;
31
37
  var ACTIVE_USE_DAYS = 7;
38
+ var V2_COLLECTION_PATTERNS = [
39
+ /^clawvault$/i,
40
+ /^vault$/i,
41
+ /^memory$/i,
42
+ /^notes$/i
43
+ ];
32
44
  function daysSince(date, now = Date.now()) {
33
45
  return Math.max(0, Math.floor((now - date.getTime()) / DAY_MS));
34
46
  }
@@ -71,6 +83,124 @@ function hasClawvaultPathConfig(paths) {
71
83
  }
72
84
  return false;
73
85
  }
86
+ function isLikelyV2CollectionName(name) {
87
+ return V2_COLLECTION_PATTERNS.some((pattern) => pattern.test(name));
88
+ }
89
+ function checkQmdCollectionExists(collections, expectedName) {
90
+ const found = collections.find((c) => c.name === expectedName);
91
+ return { exists: !!found, collection: found };
92
+ }
93
+ function checkCollectionPathMatches(collection, expectedRoot) {
94
+ if (!collection.root) return false;
95
+ const normalizedCollectionRoot = path.resolve(collection.root);
96
+ const normalizedExpectedRoot = path.resolve(expectedRoot);
97
+ return normalizedCollectionRoot === normalizedExpectedRoot;
98
+ }
99
+ function detectMigrationIssues(vaultPath, configuredCollection, configuredRoot) {
100
+ const issues = [];
101
+ if (!hasQmd()) {
102
+ return issues;
103
+ }
104
+ let collections;
105
+ try {
106
+ collections = listQmdCollections();
107
+ } catch {
108
+ return issues;
109
+ }
110
+ const vaultConfig = loadVaultQmdConfig(vaultPath);
111
+ const expectedCollection = configuredCollection || vaultConfig.qmdCollection;
112
+ const expectedRoot = configuredRoot || vaultConfig.qmdRoot;
113
+ const { exists, collection } = checkQmdCollectionExists(collections, expectedCollection);
114
+ if (!exists) {
115
+ const potentialV2Collections = collections.filter(
116
+ (c) => isLikelyV2CollectionName(c.name) && c.root && path.resolve(c.root) === path.resolve(expectedRoot)
117
+ );
118
+ if (potentialV2Collections.length > 0) {
119
+ issues.push({
120
+ type: "stale_collection_name",
121
+ description: `Found v2-style collection "${potentialV2Collections[0].name}" that should be renamed to "${expectedCollection}"`,
122
+ autoFixable: true,
123
+ details: {
124
+ oldName: potentialV2Collections[0].name,
125
+ newName: expectedCollection,
126
+ root: potentialV2Collections[0].root
127
+ }
128
+ });
129
+ } else {
130
+ issues.push({
131
+ type: "missing_qmd_collection",
132
+ description: `qmd collection "${expectedCollection}" does not exist`,
133
+ autoFixable: true,
134
+ details: {
135
+ collectionName: expectedCollection,
136
+ expectedRoot
137
+ }
138
+ });
139
+ }
140
+ } else if (collection && !checkCollectionPathMatches(collection, expectedRoot)) {
141
+ issues.push({
142
+ type: "wrong_vault_path",
143
+ description: `Collection "${expectedCollection}" points to "${collection.root}" but vault is at "${expectedRoot}"`,
144
+ autoFixable: true,
145
+ details: {
146
+ collectionName: expectedCollection,
147
+ currentRoot: collection.root,
148
+ expectedRoot
149
+ }
150
+ });
151
+ }
152
+ const orphanedCollections = collections.filter((c) => {
153
+ if (c.name === expectedCollection) return false;
154
+ if (!c.root) return false;
155
+ const collectionRoot = path.resolve(c.root);
156
+ const vaultRoot = path.resolve(expectedRoot);
157
+ return collectionRoot === vaultRoot || collectionRoot.startsWith(vaultRoot + path.sep);
158
+ });
159
+ for (const orphan of orphanedCollections) {
160
+ issues.push({
161
+ type: "orphaned_collection",
162
+ description: `Orphaned collection "${orphan.name}" points to vault path but is not the configured collection`,
163
+ autoFixable: true,
164
+ details: {
165
+ collectionName: orphan.name,
166
+ root: orphan.root
167
+ }
168
+ });
169
+ }
170
+ const configPath = path.join(vaultPath, ".clawvault.json");
171
+ if (fs.existsSync(configPath)) {
172
+ try {
173
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
174
+ if (!config.qmdCollection || !config.qmdRoot) {
175
+ issues.push({
176
+ type: "missing_qmd_config",
177
+ description: "Vault config is missing qmdCollection or qmdRoot settings",
178
+ autoFixable: true,
179
+ details: {
180
+ hasQmdCollection: !!config.qmdCollection,
181
+ hasQmdRoot: !!config.qmdRoot
182
+ }
183
+ });
184
+ }
185
+ } catch {
186
+ issues.push({
187
+ type: "legacy_config_format",
188
+ description: "Unable to parse .clawvault.json - may need migration",
189
+ autoFixable: false
190
+ });
191
+ }
192
+ }
193
+ return issues;
194
+ }
195
+ function migrationIssuesToChecks(issues) {
196
+ return issues.map((issue) => ({
197
+ label: `migration: ${issue.type.replace(/_/g, " ")}`,
198
+ status: "warn",
199
+ detail: issue.description,
200
+ hint: issue.autoFixable ? "Run `clawvault migrate` to auto-fix this issue." : "Manual intervention required.",
201
+ category: "migration"
202
+ }));
203
+ }
74
204
  async function resolveVault(vaultPath) {
75
205
  if (vaultPath) {
76
206
  const vault = new ClawVault(path.resolve(vaultPath));
@@ -93,13 +223,15 @@ async function doctor(vaultPath) {
93
223
  const checks = [];
94
224
  let warnings = 0;
95
225
  let errors = 0;
226
+ const migrationIssues = [];
96
227
  const compatReport = checkOpenClawCompatibility();
97
228
  if (compatReport.errors > 0) {
98
229
  checks.push({
99
230
  label: "OpenClaw compatibility",
100
231
  status: "error",
101
232
  detail: `${compatReport.errors} error(s), ${compatReport.warnings} warning(s)`,
102
- hint: "Run `clawvault compat` for full compatibility diagnostics."
233
+ hint: "Run `clawvault compat` for full compatibility diagnostics.",
234
+ category: "system"
103
235
  });
104
236
  errors++;
105
237
  } else if (compatReport.warnings > 0) {
@@ -107,22 +239,29 @@ async function doctor(vaultPath) {
107
239
  label: "OpenClaw compatibility",
108
240
  status: "warn",
109
241
  detail: `${compatReport.warnings} warning(s)`,
110
- hint: "Run `clawvault compat` for full compatibility diagnostics."
242
+ hint: "Run `clawvault compat` for full compatibility diagnostics.",
243
+ category: "system"
111
244
  });
112
245
  warnings++;
113
246
  } else {
114
247
  checks.push({
115
248
  label: "OpenClaw compatibility",
116
- status: "ok"
249
+ status: "ok",
250
+ category: "system"
117
251
  });
118
252
  }
119
253
  if (hasQmd()) {
120
- checks.push({ label: "qmd installed", status: "ok" });
254
+ checks.push({ label: "qmd installed", status: "ok", category: "system" });
121
255
  } else {
122
256
  checks.push({
123
257
  label: "qmd installed",
124
258
  status: "error",
125
- hint: "Install qmd to enable ClawVault commands."
259
+ detail: "qmd binary not found in PATH",
260
+ hint: `Install qmd to enable ClawVault search and indexing:
261
+ ${QMD_INSTALL_COMMAND}
262
+
263
+ For more information: ${QMD_INSTALL_URL}`,
264
+ category: "system"
126
265
  });
127
266
  errors++;
128
267
  }
@@ -131,44 +270,57 @@ async function doctor(vaultPath) {
131
270
  checks.push({
132
271
  label: "CLAWVAULT_PATH in shell config",
133
272
  status: "ok",
134
- detail: shellConfigs.map((p) => path.basename(p)).join(", ")
273
+ detail: shellConfigs.map((p) => path.basename(p)).join(", "),
274
+ category: "system"
135
275
  });
136
276
  } else {
137
277
  checks.push({
138
278
  label: "CLAWVAULT_PATH in shell config",
139
279
  status: "warn",
140
- hint: "Run `clawvault shell-init` and add it to your shell rc."
280
+ hint: "Run `clawvault shell-init` and add it to your shell rc.",
281
+ category: "system"
141
282
  });
142
283
  warnings++;
143
284
  }
144
285
  if (!hasQmd()) {
145
- return { vaultPath, checks, warnings, errors };
286
+ return { vaultPath, checks, warnings, errors, migrationIssues };
146
287
  }
147
288
  let vault;
148
289
  try {
149
290
  vault = await resolveVault(vaultPath);
150
- checks.push({ label: "vault found", status: "ok", detail: vault.getPath() });
291
+ checks.push({ label: "vault found", status: "ok", detail: vault.getPath(), category: "system" });
151
292
  } catch (err) {
152
293
  checks.push({
153
294
  label: "vault found",
154
295
  status: "error",
155
- detail: err?.message || "Unable to locate vault"
296
+ detail: err?.message || "Unable to locate vault",
297
+ category: "system"
156
298
  });
157
299
  errors++;
158
- return { vaultPath, checks, warnings, errors };
300
+ return { vaultPath, checks, warnings, errors, migrationIssues };
159
301
  }
302
+ const detectedMigrationIssues = detectMigrationIssues(
303
+ vault.getPath(),
304
+ vault.getQmdCollection(),
305
+ vault.getQmdRoot()
306
+ );
307
+ migrationIssues.push(...detectedMigrationIssues);
308
+ const migrationChecks = migrationIssuesToChecks(detectedMigrationIssues);
309
+ checks.push(...migrationChecks);
310
+ warnings += migrationChecks.length;
160
311
  const stats = await vault.stats();
161
312
  const documents = await vault.list();
162
313
  const handoffs = await vault.list("handoffs");
163
314
  const inbox = await vault.list("inbox");
164
315
  const qmdCollection = vault.getQmdCollection();
165
316
  if (qmdCollection) {
166
- checks.push({ label: "qmd collection configured", status: "ok", detail: qmdCollection });
317
+ checks.push({ label: "qmd collection configured", status: "ok", detail: qmdCollection, category: "system" });
167
318
  } else {
168
319
  checks.push({
169
320
  label: "qmd collection configured",
170
321
  status: "warn",
171
- hint: "Set qmd collection in .clawvault.json"
322
+ hint: "Set qmd collection in .clawvault.json or run `clawvault migrate`.",
323
+ category: "system"
172
324
  });
173
325
  warnings++;
174
326
  }
@@ -180,7 +332,8 @@ async function doctor(vaultPath) {
180
332
  label: "memory graph index",
181
333
  status: "warn",
182
334
  detail: "No graph index found",
183
- hint: "Run `clawvault graph --refresh` to build .clawvault/graph-index.json."
335
+ hint: "Run `clawvault graph --refresh` to build .clawvault/graph-index.json.",
336
+ category: "health"
184
337
  });
185
338
  warnings++;
186
339
  } else {
@@ -192,14 +345,16 @@ async function doctor(vaultPath) {
192
345
  label: "memory graph index",
193
346
  status: "warn",
194
347
  detail: `Stale graph index (generated ${generatedAge} ago)`,
195
- hint: "Run `clawvault graph --refresh` to resync index."
348
+ hint: "Run `clawvault graph --refresh` to resync index.",
349
+ category: "health"
196
350
  });
197
351
  warnings++;
198
352
  } else {
199
353
  checks.push({
200
354
  label: "memory graph index",
201
355
  status: "ok",
202
- detail: `${graphIndex.graph.stats.nodeCount} nodes, ${graphIndex.graph.stats.edgeCount} edges`
356
+ detail: `${graphIndex.graph.stats.nodeCount} nodes, ${graphIndex.graph.stats.edgeCount} edges`,
357
+ category: "health"
203
358
  });
204
359
  }
205
360
  }
@@ -208,7 +363,8 @@ async function doctor(vaultPath) {
208
363
  checks.push({
209
364
  label: "recent handoff",
210
365
  status: "warn",
211
- hint: "Run `clawvault sleep` at the end of sessions."
366
+ hint: "Run `clawvault sleep` at the end of sessions.",
367
+ category: "health"
212
368
  });
213
369
  warnings++;
214
370
  } else {
@@ -220,14 +376,16 @@ async function doctor(vaultPath) {
220
376
  label: "recent handoff",
221
377
  status: "warn",
222
378
  detail: `Last handoff ${ageLabel} ago`,
223
- hint: "Run `clawvault sleep` before long pauses."
379
+ hint: "Run `clawvault sleep` before long pauses.",
380
+ category: "health"
224
381
  });
225
382
  warnings++;
226
383
  } else {
227
384
  checks.push({
228
385
  label: "recent handoff",
229
386
  status: "ok",
230
- detail: `Last handoff ${ageLabel} ago`
387
+ detail: `Last handoff ${ageLabel} ago`,
388
+ category: "health"
231
389
  });
232
390
  }
233
391
  }
@@ -237,7 +395,8 @@ async function doctor(vaultPath) {
237
395
  checks.push({
238
396
  label: "checkpoint freshness",
239
397
  status: "warn",
240
- detail: checkpointInfo.error
398
+ detail: checkpointInfo.error,
399
+ category: "health"
241
400
  });
242
401
  warnings++;
243
402
  } else if (!checkpointInfo.timestamp) {
@@ -247,7 +406,8 @@ async function doctor(vaultPath) {
247
406
  label: "checkpoint freshness",
248
407
  status,
249
408
  detail: activeUse ? "No checkpoint found" : "No checkpoint found (vault appears inactive)",
250
- hint: activeUse ? "Run `clawvault checkpoint` during heavy work." : void 0
409
+ hint: activeUse ? "Run `clawvault checkpoint` during heavy work." : void 0,
410
+ category: "health"
251
411
  });
252
412
  } else {
253
413
  const checkpointDate = new Date(checkpointInfo.timestamp);
@@ -258,14 +418,16 @@ async function doctor(vaultPath) {
258
418
  label: "checkpoint freshness",
259
419
  status: "warn",
260
420
  detail: `Last checkpoint ${ageLabel} ago`,
261
- hint: "Checkpoint at least once per active day."
421
+ hint: "Checkpoint at least once per active day.",
422
+ category: "health"
262
423
  });
263
424
  warnings++;
264
425
  } else {
265
426
  checks.push({
266
427
  label: "checkpoint freshness",
267
428
  status: "ok",
268
- detail: `Last checkpoint ${ageLabel} ago`
429
+ detail: `Last checkpoint ${ageLabel} ago`,
430
+ category: "health"
269
431
  });
270
432
  }
271
433
  }
@@ -275,13 +437,15 @@ async function doctor(vaultPath) {
275
437
  label: "observer freshness",
276
438
  status: "warn",
277
439
  detail: `${observerStaleness.staleCount} stale session cursor(s); oldest ${formatAge(observerStaleness.oldestMs)} ago`,
278
- hint: "Run `clawvault observe --cron` and verify cron/hook scheduling."
440
+ hint: "Run `clawvault observe --cron` and verify cron/hook scheduling.",
441
+ category: "health"
279
442
  });
280
443
  warnings++;
281
444
  } else {
282
445
  checks.push({
283
446
  label: "observer freshness",
284
- status: "ok"
447
+ status: "ok",
448
+ category: "health"
285
449
  });
286
450
  }
287
451
  const linkScan = scanVaultLinks(vault.getPath());
@@ -290,14 +454,16 @@ async function doctor(vaultPath) {
290
454
  label: "orphan links",
291
455
  status: "warn",
292
456
  detail: `${linkScan.orphans.length} orphan link(s)`,
293
- hint: "Run `clawvault link --orphans` to review."
457
+ hint: "Run `clawvault link --orphans` to review.",
458
+ category: "health"
294
459
  });
295
460
  warnings++;
296
461
  } else {
297
462
  checks.push({
298
463
  label: "orphan links",
299
464
  status: "ok",
300
- detail: `${linkScan.orphans.length} orphan link(s)`
465
+ detail: `${linkScan.orphans.length} orphan link(s)`,
466
+ category: "health"
301
467
  });
302
468
  }
303
469
  if (inbox.length > 5) {
@@ -305,14 +471,16 @@ async function doctor(vaultPath) {
305
471
  label: "inbox backlog",
306
472
  status: "warn",
307
473
  detail: `${inbox.length} inbox item(s) pending`,
308
- hint: "Process inbox items to keep memory tidy."
474
+ hint: "Process inbox items to keep memory tidy.",
475
+ category: "health"
309
476
  });
310
477
  warnings++;
311
478
  } else {
312
479
  checks.push({
313
480
  label: "inbox backlog",
314
481
  status: "ok",
315
- detail: `${inbox.length} inbox item(s) pending`
482
+ detail: `${inbox.length} inbox item(s) pending`,
483
+ category: "health"
316
484
  });
317
485
  }
318
486
  if (stats.documents < 5) {
@@ -320,11 +488,12 @@ async function doctor(vaultPath) {
320
488
  label: "vault activity",
321
489
  status: "warn",
322
490
  detail: `${stats.documents} total documents`,
323
- hint: "Start capturing decisions, lessons, and projects."
491
+ hint: "Start capturing decisions, lessons, and projects.",
492
+ category: "health"
324
493
  });
325
494
  warnings++;
326
495
  }
327
- return { vaultPath: vault.getPath(), checks, warnings, errors };
496
+ return { vaultPath: vault.getPath(), checks, warnings, errors, migrationIssues };
328
497
  }
329
498
 
330
499
  export {
@@ -0,0 +1,207 @@
1
+ import {
2
+ __export
3
+ } from "./chunk-2ZDO52B4.js";
4
+
5
+ // src/workgraph/registry.ts
6
+ var registry_exports = {};
7
+ __export(registry_exports, {
8
+ defineType: () => defineType,
9
+ extendType: () => extendType,
10
+ getType: () => getType,
11
+ listTypes: () => listTypes,
12
+ loadRegistry: () => loadRegistry,
13
+ registryPath: () => registryPath,
14
+ saveRegistry: () => saveRegistry
15
+ });
16
+ import fs from "fs";
17
+ import path from "path";
18
+ var REGISTRY_FILE = ".clawvault/registry.json";
19
+ var CURRENT_VERSION = 1;
20
+ var BUILT_IN_TYPES = [
21
+ {
22
+ name: "thread",
23
+ description: "A unit of coordinated work. The core workgraph node.",
24
+ directory: "threads",
25
+ builtIn: true,
26
+ createdAt: "2026-01-01T00:00:00.000Z",
27
+ createdBy: "system",
28
+ fields: {
29
+ title: { type: "string", required: true, description: "What this thread is about" },
30
+ goal: { type: "string", required: true, description: "What success looks like" },
31
+ status: { type: "string", required: true, default: "open", description: "open | active | blocked | done | cancelled" },
32
+ owner: { type: "string", description: "Agent that claimed this thread" },
33
+ priority: { type: "string", default: "medium", description: "urgent | high | medium | low" },
34
+ deps: { type: "list", default: [], description: "Wiki-link refs to threads this depends on" },
35
+ parent: { type: "ref", description: "Parent thread (if decomposed from a larger thread)" },
36
+ context_refs: { type: "list", default: [], description: "Wiki-link refs to vault docs that inform this work" },
37
+ tags: { type: "list", default: [], description: "Freeform tags" },
38
+ created: { type: "date", required: true },
39
+ updated: { type: "date", required: true }
40
+ }
41
+ },
42
+ {
43
+ name: "space",
44
+ description: "A workspace boundary that groups related threads and sets context.",
45
+ directory: "spaces",
46
+ builtIn: true,
47
+ createdAt: "2026-01-01T00:00:00.000Z",
48
+ createdBy: "system",
49
+ fields: {
50
+ title: { type: "string", required: true, description: "Space name" },
51
+ description: { type: "string", description: "What this space is for" },
52
+ members: { type: "list", default: [], description: "Agent names that participate" },
53
+ thread_refs: { type: "list", default: [], description: "Wiki-link refs to threads in this space" },
54
+ tags: { type: "list", default: [], description: "Freeform tags" },
55
+ created: { type: "date", required: true },
56
+ updated: { type: "date", required: true }
57
+ }
58
+ },
59
+ {
60
+ name: "decision",
61
+ description: "A recorded decision with reasoning and context.",
62
+ directory: "decisions",
63
+ builtIn: true,
64
+ createdAt: "2026-01-01T00:00:00.000Z",
65
+ createdBy: "system",
66
+ fields: {
67
+ title: { type: "string", required: true },
68
+ date: { type: "date", required: true },
69
+ status: { type: "string", default: "active", description: "active | superseded | reverted" },
70
+ context_refs: { type: "list", default: [], description: "What informed this decision" },
71
+ tags: { type: "list", default: [] }
72
+ }
73
+ },
74
+ {
75
+ name: "lesson",
76
+ description: "A captured insight or pattern learned from experience.",
77
+ directory: "lessons",
78
+ builtIn: true,
79
+ createdAt: "2026-01-01T00:00:00.000Z",
80
+ createdBy: "system",
81
+ fields: {
82
+ title: { type: "string", required: true },
83
+ date: { type: "date", required: true },
84
+ confidence: { type: "string", default: "medium", description: "high | medium | low" },
85
+ context_refs: { type: "list", default: [] },
86
+ tags: { type: "list", default: [] }
87
+ }
88
+ },
89
+ {
90
+ name: "fact",
91
+ description: "A structured piece of knowledge with optional temporal validity.",
92
+ directory: "facts",
93
+ builtIn: true,
94
+ createdAt: "2026-01-01T00:00:00.000Z",
95
+ createdBy: "system",
96
+ fields: {
97
+ subject: { type: "string", required: true },
98
+ predicate: { type: "string", required: true },
99
+ object: { type: "string", required: true },
100
+ confidence: { type: "number", default: 1 },
101
+ valid_from: { type: "date" },
102
+ valid_until: { type: "date" },
103
+ source: { type: "ref", description: "Where this fact came from" }
104
+ }
105
+ },
106
+ {
107
+ name: "agent",
108
+ description: "A registered participant in the workgraph.",
109
+ directory: "agents",
110
+ builtIn: true,
111
+ createdAt: "2026-01-01T00:00:00.000Z",
112
+ createdBy: "system",
113
+ fields: {
114
+ name: { type: "string", required: true },
115
+ role: { type: "string", description: "What this agent specializes in" },
116
+ capabilities: { type: "list", default: [], description: "What this agent can do" },
117
+ active_threads: { type: "list", default: [], description: "Threads currently claimed" },
118
+ last_seen: { type: "date" }
119
+ }
120
+ }
121
+ ];
122
+ function registryPath(vaultPath) {
123
+ return path.join(vaultPath, REGISTRY_FILE);
124
+ }
125
+ function loadRegistry(vaultPath) {
126
+ const rPath = registryPath(vaultPath);
127
+ if (fs.existsSync(rPath)) {
128
+ const raw = fs.readFileSync(rPath, "utf-8");
129
+ const registry = JSON.parse(raw);
130
+ return ensureBuiltIns(registry);
131
+ }
132
+ return seedRegistry();
133
+ }
134
+ function saveRegistry(vaultPath, registry) {
135
+ const rPath = registryPath(vaultPath);
136
+ const dir = path.dirname(rPath);
137
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
138
+ fs.writeFileSync(rPath, JSON.stringify(registry, null, 2) + "\n", "utf-8");
139
+ }
140
+ function defineType(vaultPath, name, description, fields, actor, directory) {
141
+ const registry = loadRegistry(vaultPath);
142
+ const safeName = name.toLowerCase().replace(/[^a-z0-9_-]/g, "-");
143
+ if (registry.types[safeName]?.builtIn) {
144
+ throw new Error(`Cannot redefine built-in type "${safeName}". You can extend it with new fields instead.`);
145
+ }
146
+ const now = (/* @__PURE__ */ new Date()).toISOString();
147
+ const typeDef = {
148
+ name: safeName,
149
+ description,
150
+ fields: {
151
+ title: { type: "string", required: true },
152
+ created: { type: "date", required: true },
153
+ updated: { type: "date", required: true },
154
+ tags: { type: "list", default: [] },
155
+ ...fields
156
+ },
157
+ directory: directory ?? `${safeName}s`,
158
+ builtIn: false,
159
+ createdAt: now,
160
+ createdBy: actor
161
+ };
162
+ registry.types[safeName] = typeDef;
163
+ saveRegistry(vaultPath, registry);
164
+ return typeDef;
165
+ }
166
+ function getType(vaultPath, name) {
167
+ const registry = loadRegistry(vaultPath);
168
+ return registry.types[name];
169
+ }
170
+ function listTypes(vaultPath) {
171
+ const registry = loadRegistry(vaultPath);
172
+ return Object.values(registry.types);
173
+ }
174
+ function extendType(vaultPath, name, newFields, actor) {
175
+ const registry = loadRegistry(vaultPath);
176
+ const existing = registry.types[name];
177
+ if (!existing) throw new Error(`Type "${name}" not found in registry.`);
178
+ existing.fields = { ...existing.fields, ...newFields };
179
+ saveRegistry(vaultPath, registry);
180
+ return existing;
181
+ }
182
+ function seedRegistry() {
183
+ const types = {};
184
+ for (const t of BUILT_IN_TYPES) {
185
+ types[t.name] = t;
186
+ }
187
+ return { version: CURRENT_VERSION, types };
188
+ }
189
+ function ensureBuiltIns(registry) {
190
+ for (const t of BUILT_IN_TYPES) {
191
+ if (!registry.types[t.name]) {
192
+ registry.types[t.name] = t;
193
+ }
194
+ }
195
+ return registry;
196
+ }
197
+
198
+ export {
199
+ registryPath,
200
+ loadRegistry,
201
+ saveRegistry,
202
+ defineType,
203
+ getType,
204
+ listTypes,
205
+ extendType,
206
+ registry_exports
207
+ };
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  requestLlmCompletion,
7
7
  resolveLlmProvider
8
- } from "./chunk-HIHOUSXS.js";
8
+ } from "./chunk-YXQCA6B7.js";
9
9
  import {
10
10
  archiveObservations
11
11
  } from "./chunk-MQUJNOHK.js";
@@ -1,21 +1,21 @@
1
1
  import {
2
2
  registerTailscaleCommands
3
- } from "./chunk-THRJVD4L.js";
3
+ } from "./chunk-Y6VJKXGL.js";
4
4
  import {
5
5
  registerObserveCommand
6
- } from "./chunk-ME37YNW3.js";
6
+ } from "./chunk-P7SY3D4E.js";
7
7
  import {
8
8
  registerReflectCommand
9
- } from "./chunk-3BTHWPMB.js";
9
+ } from "./chunk-S5OJEGFG.js";
10
10
  import {
11
11
  registerContextCommand
12
- } from "./chunk-XAVB4GB4.js";
12
+ } from "./chunk-LIGHWOH6.js";
13
13
  import {
14
14
  registerEmbedCommand
15
- } from "./chunk-4QYGFWRM.js";
15
+ } from "./chunk-ECRZL5XR.js";
16
16
  import {
17
17
  registerInjectCommand
18
- } from "./chunk-4VRIMU4O.js";
18
+ } from "./chunk-YNIPYN4F.js";
19
19
 
20
20
  // src/cli/index.ts
21
21
  function registerCliCommands(program) {