clawvault 3.1.0 → 3.2.1

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 (273) hide show
  1. package/README.md +422 -141
  2. package/bin/clawvault.js +10 -2
  3. package/bin/command-registration.test.js +3 -1
  4. package/bin/command-runtime.js +9 -1
  5. package/bin/register-core-commands.js +23 -28
  6. package/bin/register-maintenance-commands.js +39 -3
  7. package/bin/register-query-commands.js +58 -29
  8. package/bin/register-tailscale-commands.js +106 -0
  9. package/bin/register-task-commands.js +18 -1
  10. package/bin/register-task-commands.test.js +16 -0
  11. package/bin/register-vault-operations-commands.js +29 -1
  12. package/bin/register-workgraph-commands.js +451 -0
  13. package/dashboard/lib/graph-diff.js +104 -0
  14. package/dashboard/lib/graph-diff.test.js +75 -0
  15. package/dashboard/lib/vault-parser.js +556 -0
  16. package/dashboard/lib/vault-parser.test.js +254 -0
  17. package/dashboard/public/app.js +796 -0
  18. package/dashboard/public/index.html +52 -0
  19. package/dashboard/public/styles.css +221 -0
  20. package/dashboard/server.js +374 -0
  21. package/dist/{chunk-C7OK5WKP.js → chunk-2JQ3O2YL.js} +4 -4
  22. package/dist/{chunk-VR5NE7PZ.js → chunk-2RAZ4ZFE.js} +1 -1
  23. package/dist/{chunk-F2JEUD4J.js → chunk-4ITRXIVT.js} +5 -7
  24. package/dist/{chunk-GUKMRGM7.js → chunk-4OXMU5S2.js} +1 -1
  25. package/dist/chunk-5PJ4STIC.js +465 -0
  26. package/dist/{chunk-62YTUT6J.js → chunk-AZYOKJYC.js} +2 -2
  27. package/dist/chunk-BSJ6RIT7.js +447 -0
  28. package/dist/chunk-ECRZL5XR.js +50 -0
  29. package/dist/chunk-ERNE2FZ5.js +189 -0
  30. package/dist/{chunk-WAZ3NLWL.js → chunk-F55HGNU4.js} +0 -47
  31. package/dist/{chunk-VGLOTGAS.js → chunk-FAKNOB7Y.js} +2 -2
  32. package/dist/{chunk-QK3UCXWL.js → chunk-FHFUXL6G.js} +2 -2
  33. package/dist/chunk-GNJL4YGR.js +79 -0
  34. package/dist/chunk-HR4KN6S2.js +152 -0
  35. package/dist/{chunk-OZ7RIXTO.js → chunk-IIOU45CK.js} +1 -1
  36. package/dist/chunk-IJBFGPCS.js +33 -0
  37. package/dist/chunk-IVRIKYFE.js +520 -0
  38. package/dist/chunk-K7PNYS45.js +93 -0
  39. package/dist/chunk-MDIH26GC.js +183 -0
  40. package/dist/{chunk-LYHGEHXG.js → chunk-MFAWT5O5.js} +0 -1
  41. package/dist/{chunk-H34S76MB.js → chunk-MNPUYCHQ.js} +6 -6
  42. package/dist/chunk-NTOPJI7W.js +207 -0
  43. package/dist/{chunk-QBLMXKF2.js → chunk-OIWVQYQF.js} +1 -1
  44. package/dist/chunk-PG56HX5T.js +154 -0
  45. package/dist/{chunk-LNJA2UGL.js → chunk-PI4WMLMG.js} +7 -84
  46. package/dist/chunk-QMHPQYUV.js +363 -0
  47. package/dist/{chunk-H62BP7RI.js → chunk-QPDDIHXE.js} +209 -43
  48. package/dist/{chunk-N2AXRYLC.js → chunk-QWQ3TIKS.js} +1 -1
  49. package/dist/{chunk-3DHXQHYG.js → chunk-R2MIW5G7.js} +1 -1
  50. package/dist/{chunk-SJSFRIYS.js → chunk-S5OJEGFG.js} +2 -2
  51. package/dist/chunk-SS4B7P7V.js +99 -0
  52. package/dist/chunk-TIGW564L.js +628 -0
  53. package/dist/chunk-U67V476Y.js +35 -0
  54. package/dist/{chunk-JY6FYXIT.js → chunk-UCQAOZHW.js} +6 -11
  55. package/dist/{chunk-ITPEXLHA.js → chunk-URXDAUVH.js} +24 -5
  56. package/dist/chunk-WIOLLGAD.js +190 -0
  57. package/dist/{chunk-3WRJEKN4.js → chunk-WJVWINEM.js} +72 -8
  58. package/dist/chunk-WMGIIABP.js +15 -0
  59. package/dist/{chunk-33UGEQRT.js → chunk-X3SPPUFG.js} +151 -64
  60. package/dist/{chunk-3NSBOUT3.js → chunk-Y3TIJEBP.js} +314 -79
  61. package/dist/chunk-Y6VJKXGL.js +373 -0
  62. package/dist/{chunk-LI4O6NVK.js → chunk-YDWHS4LJ.js} +49 -9
  63. package/dist/{chunk-U55BGUAU.js → chunk-YNIPYN4F.js} +5 -5
  64. package/dist/chunk-YXQCA6B7.js +226 -0
  65. package/dist/cli/index.js +26 -22
  66. package/dist/commands/archive.js +3 -3
  67. package/dist/commands/backlog.js +3 -3
  68. package/dist/commands/blocked.js +3 -3
  69. package/dist/commands/canvas.d.ts +15 -0
  70. package/dist/commands/canvas.js +200 -0
  71. package/dist/commands/checkpoint.js +2 -2
  72. package/dist/commands/compat.js +2 -2
  73. package/dist/commands/context.js +7 -5
  74. package/dist/commands/doctor.d.ts +11 -7
  75. package/dist/commands/doctor.js +16 -14
  76. package/dist/commands/embed.js +5 -6
  77. package/dist/commands/entities.js +2 -2
  78. package/dist/commands/graph.js +3 -3
  79. package/dist/commands/inject.d.ts +1 -1
  80. package/dist/commands/inject.js +4 -5
  81. package/dist/commands/kanban.js +4 -4
  82. package/dist/commands/link.js +2 -2
  83. package/dist/commands/migrate-observations.js +4 -4
  84. package/dist/commands/observe.d.ts +0 -1
  85. package/dist/commands/observe.js +13 -12
  86. package/dist/commands/project.js +5 -5
  87. package/dist/commands/rebuild-embeddings.d.ts +21 -0
  88. package/dist/commands/rebuild-embeddings.js +91 -0
  89. package/dist/commands/rebuild.js +12 -11
  90. package/dist/commands/recover.js +3 -3
  91. package/dist/commands/reflect.js +6 -7
  92. package/dist/commands/repair-session.js +1 -1
  93. package/dist/commands/replay.js +14 -14
  94. package/dist/commands/session-recap.js +1 -1
  95. package/dist/commands/setup.d.ts +2 -89
  96. package/dist/commands/setup.js +3 -21
  97. package/dist/commands/shell-init.js +1 -1
  98. package/dist/commands/sleep.d.ts +1 -1
  99. package/dist/commands/sleep.js +18 -17
  100. package/dist/commands/status.d.ts +2 -0
  101. package/dist/commands/status.js +40 -30
  102. package/dist/commands/sync-bd.d.ts +10 -0
  103. package/dist/commands/sync-bd.js +10 -0
  104. package/dist/commands/tailscale.d.ts +52 -0
  105. package/dist/commands/tailscale.js +26 -0
  106. package/dist/commands/task.js +4 -4
  107. package/dist/commands/template.js +2 -2
  108. package/dist/commands/wake.d.ts +1 -1
  109. package/dist/commands/wake.js +11 -10
  110. package/dist/index.d.ts +334 -191
  111. package/dist/index.js +432 -108
  112. package/dist/{inject-Bzi5E-By.d.ts → inject-DYUrDqQO.d.ts} +3 -3
  113. package/dist/ledger-B7g7jhqG.d.ts +44 -0
  114. package/dist/lib/auto-linker.js +1 -1
  115. package/dist/lib/canvas-layout.d.ts +115 -0
  116. package/dist/lib/canvas-layout.js +35 -0
  117. package/dist/lib/config.d.ts +27 -3
  118. package/dist/lib/config.js +4 -2
  119. package/dist/lib/entity-index.js +1 -1
  120. package/dist/lib/project-utils.js +4 -4
  121. package/dist/lib/session-repair.js +1 -1
  122. package/dist/lib/session-utils.js +1 -1
  123. package/dist/lib/tailscale.d.ts +225 -0
  124. package/dist/lib/tailscale.js +50 -0
  125. package/dist/lib/task-utils.js +3 -3
  126. package/dist/lib/template-engine.js +1 -1
  127. package/dist/lib/webdav.d.ts +109 -0
  128. package/dist/lib/webdav.js +35 -0
  129. package/dist/plugin/index.d.ts +344 -28
  130. package/dist/plugin/index.js +3919 -227
  131. package/dist/registry-BR4326o0.d.ts +30 -0
  132. package/dist/store-CA-6sKCJ.d.ts +34 -0
  133. package/dist/thread-B9LhXNU0.d.ts +41 -0
  134. package/dist/{types-Y2_Um2Ls.d.ts → types-BbWJoC1c.d.ts} +1 -44
  135. package/dist/workgraph/index.d.ts +5 -0
  136. package/dist/workgraph/index.js +23 -0
  137. package/dist/workgraph/ledger.d.ts +2 -0
  138. package/dist/workgraph/ledger.js +25 -0
  139. package/dist/workgraph/registry.d.ts +2 -0
  140. package/dist/workgraph/registry.js +19 -0
  141. package/dist/workgraph/store.d.ts +2 -0
  142. package/dist/workgraph/store.js +25 -0
  143. package/dist/workgraph/thread.d.ts +2 -0
  144. package/dist/workgraph/thread.js +25 -0
  145. package/dist/workgraph/types.d.ts +54 -0
  146. package/dist/workgraph/types.js +7 -0
  147. package/hooks/clawvault/HOOK.md +113 -0
  148. package/hooks/clawvault/handler.js +1559 -0
  149. package/hooks/clawvault/handler.test.js +510 -0
  150. package/hooks/clawvault/openclaw.plugin.json +72 -0
  151. package/openclaw.plugin.json +235 -30
  152. package/package.json +20 -20
  153. package/dist/chunk-3RG5ZIWI.js +0 -10
  154. package/dist/chunk-3ZIH425O.js +0 -871
  155. package/dist/chunk-6U6MK36V.js +0 -205
  156. package/dist/chunk-CMB7UL7C.js +0 -327
  157. package/dist/chunk-D2H45LON.js +0 -1074
  158. package/dist/chunk-E7MFQB6D.js +0 -163
  159. package/dist/chunk-GQSLDZTS.js +0 -560
  160. package/dist/chunk-MFM6K7PU.js +0 -374
  161. package/dist/chunk-MXSSG3QU.js +0 -42
  162. package/dist/chunk-OCGVIN3L.js +0 -88
  163. package/dist/chunk-PAH27GSN.js +0 -108
  164. package/dist/chunk-YCUNCH2I.js +0 -78
  165. package/dist/cli/index.cjs +0 -8584
  166. package/dist/cli/index.d.cts +0 -5
  167. package/dist/commands/archive.cjs +0 -287
  168. package/dist/commands/archive.d.cts +0 -11
  169. package/dist/commands/backlog.cjs +0 -721
  170. package/dist/commands/backlog.d.cts +0 -53
  171. package/dist/commands/blocked.cjs +0 -204
  172. package/dist/commands/blocked.d.cts +0 -26
  173. package/dist/commands/checkpoint.cjs +0 -244
  174. package/dist/commands/checkpoint.d.cts +0 -41
  175. package/dist/commands/compat.cjs +0 -294
  176. package/dist/commands/compat.d.cts +0 -28
  177. package/dist/commands/context.cjs +0 -2990
  178. package/dist/commands/context.d.cts +0 -2
  179. package/dist/commands/doctor.cjs +0 -2986
  180. package/dist/commands/doctor.d.cts +0 -21
  181. package/dist/commands/embed.cjs +0 -232
  182. package/dist/commands/embed.d.cts +0 -17
  183. package/dist/commands/entities.cjs +0 -141
  184. package/dist/commands/entities.d.cts +0 -7
  185. package/dist/commands/graph.cjs +0 -501
  186. package/dist/commands/graph.d.cts +0 -21
  187. package/dist/commands/inject.cjs +0 -1636
  188. package/dist/commands/inject.d.cts +0 -2
  189. package/dist/commands/kanban.cjs +0 -884
  190. package/dist/commands/kanban.d.cts +0 -63
  191. package/dist/commands/link.cjs +0 -965
  192. package/dist/commands/link.d.cts +0 -11
  193. package/dist/commands/migrate-observations.cjs +0 -362
  194. package/dist/commands/migrate-observations.d.cts +0 -19
  195. package/dist/commands/observe.cjs +0 -4099
  196. package/dist/commands/observe.d.cts +0 -23
  197. package/dist/commands/project.cjs +0 -1341
  198. package/dist/commands/project.d.cts +0 -85
  199. package/dist/commands/rebuild.cjs +0 -3136
  200. package/dist/commands/rebuild.d.cts +0 -11
  201. package/dist/commands/recover.cjs +0 -361
  202. package/dist/commands/recover.d.cts +0 -38
  203. package/dist/commands/reflect.cjs +0 -1008
  204. package/dist/commands/reflect.d.cts +0 -11
  205. package/dist/commands/repair-session.cjs +0 -457
  206. package/dist/commands/repair-session.d.cts +0 -38
  207. package/dist/commands/replay.cjs +0 -4103
  208. package/dist/commands/replay.d.cts +0 -16
  209. package/dist/commands/session-recap.cjs +0 -353
  210. package/dist/commands/session-recap.d.cts +0 -27
  211. package/dist/commands/setup.cjs +0 -1278
  212. package/dist/commands/setup.d.cts +0 -99
  213. package/dist/commands/shell-init.cjs +0 -75
  214. package/dist/commands/shell-init.d.cts +0 -7
  215. package/dist/commands/sleep.cjs +0 -6029
  216. package/dist/commands/sleep.d.cts +0 -36
  217. package/dist/commands/status.cjs +0 -2737
  218. package/dist/commands/status.d.cts +0 -52
  219. package/dist/commands/task.cjs +0 -1236
  220. package/dist/commands/task.d.cts +0 -97
  221. package/dist/commands/template.cjs +0 -457
  222. package/dist/commands/template.d.cts +0 -36
  223. package/dist/commands/wake.cjs +0 -2627
  224. package/dist/commands/wake.d.cts +0 -22
  225. package/dist/context-BUGaWpyL.d.cts +0 -46
  226. package/dist/index.cjs +0 -12373
  227. package/dist/index.d.cts +0 -854
  228. package/dist/inject-Bzi5E-By.d.cts +0 -137
  229. package/dist/lib/auto-linker.cjs +0 -176
  230. package/dist/lib/auto-linker.d.cts +0 -26
  231. package/dist/lib/config.cjs +0 -78
  232. package/dist/lib/config.d.cts +0 -11
  233. package/dist/lib/entity-index.cjs +0 -84
  234. package/dist/lib/entity-index.d.cts +0 -26
  235. package/dist/lib/project-utils.cjs +0 -864
  236. package/dist/lib/project-utils.d.cts +0 -97
  237. package/dist/lib/session-repair.cjs +0 -239
  238. package/dist/lib/session-repair.d.cts +0 -110
  239. package/dist/lib/session-utils.cjs +0 -209
  240. package/dist/lib/session-utils.d.cts +0 -63
  241. package/dist/lib/task-utils.cjs +0 -1137
  242. package/dist/lib/task-utils.d.cts +0 -208
  243. package/dist/lib/template-engine.cjs +0 -47
  244. package/dist/lib/template-engine.d.cts +0 -11
  245. package/dist/plugin/index.cjs +0 -1907
  246. package/dist/plugin/index.d.cts +0 -36
  247. package/dist/plugin/inject.cjs +0 -356
  248. package/dist/plugin/inject.d.cts +0 -54
  249. package/dist/plugin/inject.d.ts +0 -54
  250. package/dist/plugin/inject.js +0 -17
  251. package/dist/plugin/observe.cjs +0 -631
  252. package/dist/plugin/observe.d.cts +0 -39
  253. package/dist/plugin/observe.d.ts +0 -39
  254. package/dist/plugin/observe.js +0 -18
  255. package/dist/plugin/templates.cjs +0 -593
  256. package/dist/plugin/templates.d.cts +0 -52
  257. package/dist/plugin/templates.d.ts +0 -52
  258. package/dist/plugin/templates.js +0 -25
  259. package/dist/plugin/types.cjs +0 -18
  260. package/dist/plugin/types.d.cts +0 -209
  261. package/dist/plugin/types.d.ts +0 -209
  262. package/dist/plugin/types.js +0 -0
  263. package/dist/plugin/vault.cjs +0 -927
  264. package/dist/plugin/vault.d.cts +0 -68
  265. package/dist/plugin/vault.d.ts +0 -68
  266. package/dist/plugin/vault.js +0 -22
  267. package/dist/types-Y2_Um2Ls.d.cts +0 -205
  268. package/templates/memory-event.md +0 -67
  269. package/templates/party.md +0 -63
  270. package/templates/primitive-registry.yaml +0 -551
  271. package/templates/run.md +0 -68
  272. package/templates/trigger.md +0 -68
  273. package/templates/workspace.md +0 -50
@@ -3,13 +3,13 @@ import {
3
3
  } from "./chunk-P5EPF6MB.js";
4
4
  import {
5
5
  observeActiveSessions
6
- } from "./chunk-LNJA2UGL.js";
6
+ } from "./chunk-PI4WMLMG.js";
7
7
  import {
8
8
  Observer
9
- } from "./chunk-3NSBOUT3.js";
9
+ } from "./chunk-Y3TIJEBP.js";
10
10
  import {
11
11
  resolveVaultPath
12
- } from "./chunk-MXSSG3QU.js";
12
+ } from "./chunk-GNJL4YGR.js";
13
13
  import {
14
14
  getObservationPath
15
15
  } from "./chunk-Z2XBWN7A.js";
@@ -291,8 +291,7 @@ async function observeCommand(options) {
291
291
  threshold: options.threshold,
292
292
  reflectThreshold: options.reflectThreshold,
293
293
  model: options.model,
294
- extractTasks: options.extractTasks,
295
- maxSessions: options.maxSessions
294
+ extractTasks: options.extractTasks
296
295
  });
297
296
  const failedSessionCount = result.failedSessionCount ?? 0;
298
297
  if (options.cron) {
@@ -380,7 +379,7 @@ async function observeCommand(options) {
380
379
  await watchSessions(observer, watchPath);
381
380
  }
382
381
  function registerObserveCommand(program) {
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) => {
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) => {
384
383
  await observeCommand({
385
384
  watch: rawOptions.watch,
386
385
  active: rawOptions.active,
@@ -389,7 +388,6 @@ function registerObserveCommand(program) {
389
388
  minNew: rawOptions.minNew ? parsePositiveInteger(rawOptions.minNew, "min-new") : void 0,
390
389
  sessionsDir: rawOptions.sessionsDir,
391
390
  dryRun: rawOptions.dryRun,
392
- maxSessions: rawOptions.maxSessions ? parsePositiveInteger(rawOptions.maxSessions, "max-sessions") : void 0,
393
391
  threshold: parsePositiveInteger(rawOptions.threshold, "threshold"),
394
392
  reflectThreshold: parsePositiveInteger(rawOptions.reflectThreshold, "reflect-threshold"),
395
393
  model: rawOptions.model,
@@ -2,7 +2,7 @@ import {
2
2
  listTasks,
3
3
  readTask,
4
4
  updateTask
5
- } from "./chunk-N2AXRYLC.js";
5
+ } from "./chunk-QWQ3TIKS.js";
6
6
 
7
7
  // src/commands/kanban.ts
8
8
  import * as fs from "fs";
@@ -0,0 +1,465 @@
1
+ // src/lib/search.ts
2
+ import { execFileSync, spawnSync } from "child_process";
3
+ import * as fs from "fs";
4
+ import * as path from "path";
5
+ var QMD_INSTALL_URL = "https://github.com/tobi/qmd";
6
+ var QMD_INSTALL_COMMAND = "bun install -g github:tobi/qmd";
7
+ var QMD_INDEX_ENV_VAR = "CLAWVAULT_QMD_INDEX";
8
+ var QMD_ERROR_MESSAGES = {
9
+ NOT_INSTALLED: {
10
+ code: "NOT_INSTALLED",
11
+ message: "qmd is not installed",
12
+ hint: `Install qmd to enable ClawVault search and indexing:
13
+ ${QMD_INSTALL_COMMAND}
14
+
15
+ For more information: ${QMD_INSTALL_URL}`
16
+ },
17
+ NOT_CONFIGURED: {
18
+ code: "NOT_CONFIGURED",
19
+ message: "qmd collection is not configured",
20
+ hint: "Run `clawvault doctor` to diagnose configuration issues, or `clawvault migrate` to fix common setup problems."
21
+ },
22
+ COLLECTION_NOT_FOUND: {
23
+ code: "COLLECTION_NOT_FOUND",
24
+ message: "qmd collection not found",
25
+ hint: "The configured qmd collection does not exist. Run `clawvault migrate` to recreate it, or `qmd collection add <name> <path>` manually."
26
+ },
27
+ EXECUTION_FAILED: {
28
+ code: "EXECUTION_FAILED",
29
+ message: "qmd command failed",
30
+ hint: "Run `clawvault doctor` to diagnose qmd issues."
31
+ }
32
+ };
33
+ var QmdUnavailableError = class extends Error {
34
+ code;
35
+ hint;
36
+ constructor(code = "NOT_INSTALLED", additionalContext) {
37
+ const details = QMD_ERROR_MESSAGES[code];
38
+ const fullMessage = additionalContext ? `${details.message}: ${additionalContext}` : details.message;
39
+ super(fullMessage);
40
+ this.name = "QmdUnavailableError";
41
+ this.code = code;
42
+ this.hint = details.hint;
43
+ }
44
+ toUserMessage() {
45
+ return `Error: ${this.message}
46
+
47
+ ${this.hint}`;
48
+ }
49
+ };
50
+ function getQmdErrorDetails(code) {
51
+ return QMD_ERROR_MESSAGES[code];
52
+ }
53
+ var QmdConfigurationError = class extends Error {
54
+ constructor(message, hint) {
55
+ super(message);
56
+ this.hint = hint;
57
+ this.name = "QmdConfigurationError";
58
+ }
59
+ };
60
+ function ensureJsonArgs(args) {
61
+ return args.includes("--json") ? args : [...args, "--json"];
62
+ }
63
+ function resolveQmdIndexName(indexName) {
64
+ const explicit = indexName?.trim();
65
+ if (explicit) {
66
+ return explicit;
67
+ }
68
+ const fromEnv = process.env[QMD_INDEX_ENV_VAR]?.trim();
69
+ return fromEnv || void 0;
70
+ }
71
+ function withQmdIndexArgs(args, indexName) {
72
+ if (args.includes("--index")) {
73
+ return [...args];
74
+ }
75
+ const resolvedIndexName = resolveQmdIndexName(indexName);
76
+ if (!resolvedIndexName) {
77
+ return [...args];
78
+ }
79
+ return ["--index", resolvedIndexName, ...args];
80
+ }
81
+ function tryParseJson(raw) {
82
+ try {
83
+ return JSON.parse(raw);
84
+ } catch {
85
+ return null;
86
+ }
87
+ }
88
+ function extractJsonPayload(raw) {
89
+ const start = raw.search(/[\[{]/);
90
+ if (start === -1) return null;
91
+ const end = Math.max(raw.lastIndexOf("]"), raw.lastIndexOf("}"));
92
+ if (end <= start) return null;
93
+ return raw.slice(start, end + 1);
94
+ }
95
+ function stripQmdNoise(raw) {
96
+ return raw.split("\n").filter((line) => {
97
+ const t = line.trim();
98
+ if (!t) return true;
99
+ if (t.startsWith("[node-llama-cpp]")) return false;
100
+ if (t.startsWith("Expanding query")) return false;
101
+ if (t.startsWith("Searching ") && t.endsWith("queries...")) return false;
102
+ if (/^[├└─│]/.test(t)) return false;
103
+ return true;
104
+ }).join("\n");
105
+ }
106
+ function parseQmdOutput(raw) {
107
+ const trimmed = stripQmdNoise(raw).trim();
108
+ if (!trimmed) return [];
109
+ const direct = tryParseJson(trimmed);
110
+ const extracted = direct ? null : extractJsonPayload(trimmed);
111
+ const parsed = direct ?? (extracted ? tryParseJson(extracted) : null);
112
+ if (!parsed) {
113
+ throw new Error("qmd returned non-JSON output. Ensure qmd supports --json.");
114
+ }
115
+ if (Array.isArray(parsed)) {
116
+ return parsed;
117
+ }
118
+ if (parsed && typeof parsed === "object") {
119
+ const candidate = parsed.results ?? parsed.items ?? parsed.data;
120
+ if (Array.isArray(candidate)) {
121
+ return candidate;
122
+ }
123
+ }
124
+ throw new Error("qmd returned an unexpected JSON shape.");
125
+ }
126
+ function ensureQmdAvailable() {
127
+ if (!hasQmd()) {
128
+ throw new QmdUnavailableError("NOT_INSTALLED");
129
+ }
130
+ }
131
+ function detectQmdError(output, args) {
132
+ const lowerOutput = output.toLowerCase();
133
+ if (lowerOutput.includes("missing required arguments") || lowerOutput.includes("unknown option")) {
134
+ return new QmdConfigurationError(
135
+ 'qmd does not support the search command with the expected arguments. This may indicate an incompatible qmd version or a different tool named "qmd".',
136
+ `Ensure you have the correct qmd installed: ${QMD_INSTALL_COMMAND}`
137
+ );
138
+ }
139
+ if (lowerOutput.includes("collection not found") || lowerOutput.includes("no collection")) {
140
+ const collectionArg = args.findIndex((a) => a === "-c");
141
+ const collectionName = collectionArg >= 0 && args[collectionArg + 1] ? args[collectionArg + 1] : "unknown";
142
+ return new QmdConfigurationError(
143
+ `qmd collection "${collectionName}" not found.`,
144
+ 'Run `qmd update -c <collection>` to create the collection, or check your vault\'s .clawvault.json "name" field.'
145
+ );
146
+ }
147
+ if (lowerOutput.includes("no index") || lowerOutput.includes("index not found")) {
148
+ return new QmdConfigurationError(
149
+ "qmd index not found. The vault may not be indexed yet.",
150
+ "Run `clawvault rebuild` or `qmd update` to build the search index."
151
+ );
152
+ }
153
+ if (lowerOutput.includes("embedding") && (lowerOutput.includes("not found") || lowerOutput.includes("missing"))) {
154
+ return new QmdConfigurationError(
155
+ "qmd embeddings not found. Vector search requires embeddings to be generated.",
156
+ "Run `clawvault embed` or `qmd embed` to generate embeddings for semantic search."
157
+ );
158
+ }
159
+ return null;
160
+ }
161
+ function execQmd(args, indexName) {
162
+ ensureQmdAvailable();
163
+ const finalArgs = withQmdIndexArgs(ensureJsonArgs(args), indexName);
164
+ try {
165
+ const result = execFileSync("qmd", finalArgs, {
166
+ encoding: "utf-8",
167
+ stdio: ["ignore", "pipe", "pipe"],
168
+ maxBuffer: 10 * 1024 * 1024
169
+ // 10MB
170
+ });
171
+ return parseQmdOutput(result);
172
+ } catch (err) {
173
+ if (err?.code === "ENOENT") {
174
+ throw new QmdUnavailableError("NOT_INSTALLED");
175
+ }
176
+ const output = [err?.stdout, err?.stderr].filter(Boolean).join("\n");
177
+ const detectedError = detectQmdError(output, finalArgs);
178
+ if (detectedError) {
179
+ throw detectedError;
180
+ }
181
+ if (output) {
182
+ try {
183
+ return parseQmdOutput(output);
184
+ } catch {
185
+ }
186
+ if (output.includes("collection not found") || output.includes("no such collection")) {
187
+ throw new QmdUnavailableError("COLLECTION_NOT_FOUND", output.trim());
188
+ }
189
+ }
190
+ const errorDetail = err?.message || "unknown error";
191
+ throw new QmdUnavailableError("EXECUTION_FAILED", errorDetail);
192
+ }
193
+ }
194
+ function hasQmd() {
195
+ const result = spawnSync("qmd", ["--version"], { stdio: "ignore" });
196
+ return !result.error;
197
+ }
198
+ function qmdUpdate(collection, indexName) {
199
+ ensureQmdAvailable();
200
+ const args = ["update"];
201
+ if (collection) {
202
+ args.push("-c", collection);
203
+ }
204
+ execFileSync("qmd", withQmdIndexArgs(args, indexName), { stdio: "inherit" });
205
+ }
206
+ function qmdEmbed(collection, indexName) {
207
+ ensureQmdAvailable();
208
+ const args = ["embed"];
209
+ if (collection) {
210
+ args.push("-c", collection);
211
+ }
212
+ execFileSync("qmd", withQmdIndexArgs(args, indexName), { stdio: "inherit" });
213
+ }
214
+ var SearchEngine = class {
215
+ documents = /* @__PURE__ */ new Map();
216
+ collection = "";
217
+ vaultPath = "";
218
+ collectionRoot = "";
219
+ qmdIndexName;
220
+ /**
221
+ * Set the collection name (usually vault name)
222
+ */
223
+ setCollection(name) {
224
+ this.collection = name;
225
+ }
226
+ /**
227
+ * Get the current collection name
228
+ */
229
+ getCollection() {
230
+ return this.collection;
231
+ }
232
+ /**
233
+ * Set the vault path for file resolution
234
+ */
235
+ setVaultPath(vaultPath) {
236
+ this.vaultPath = vaultPath;
237
+ }
238
+ /**
239
+ * Set the collection root for qmd:// URI resolution
240
+ */
241
+ setCollectionRoot(root) {
242
+ this.collectionRoot = path.resolve(root);
243
+ }
244
+ /**
245
+ * Set qmd index name (defaults to qmd global default when omitted)
246
+ */
247
+ setIndexName(indexName) {
248
+ this.qmdIndexName = indexName;
249
+ }
250
+ /**
251
+ * Add or update a document in the local cache
252
+ * Note: qmd indexing happens via qmd update command
253
+ */
254
+ addDocument(doc) {
255
+ this.documents.set(doc.id, doc);
256
+ }
257
+ /**
258
+ * Remove a document from the local cache
259
+ */
260
+ removeDocument(id) {
261
+ this.documents.delete(id);
262
+ }
263
+ /**
264
+ * No-op for qmd - indexing is managed externally
265
+ */
266
+ rebuildIDF() {
267
+ }
268
+ /**
269
+ * BM25 search via qmd
270
+ */
271
+ search(query, options = {}) {
272
+ return this.runQmdQuery("search", query, options);
273
+ }
274
+ /**
275
+ * Vector/semantic search via qmd vsearch
276
+ */
277
+ vsearch(query, options = {}) {
278
+ return this.runQmdQuery("vsearch", query, options);
279
+ }
280
+ /**
281
+ * Combined search with query expansion (qmd query command)
282
+ */
283
+ query(query, options = {}) {
284
+ return this.runQmdQuery("query", query, options);
285
+ }
286
+ runQmdQuery(command, query, options) {
287
+ const {
288
+ limit = 10,
289
+ minScore = 0,
290
+ category,
291
+ tags,
292
+ fullContent = false,
293
+ temporalBoost = false
294
+ } = options;
295
+ if (!query.trim()) return [];
296
+ const args = [
297
+ command,
298
+ query,
299
+ "-n",
300
+ String(limit * 2),
301
+ "--json"
302
+ ];
303
+ if (this.collection) {
304
+ args.push("-c", this.collection);
305
+ }
306
+ const qmdResults = execQmd(args, this.qmdIndexName);
307
+ return this.convertResults(qmdResults, {
308
+ limit,
309
+ minScore,
310
+ category,
311
+ tags,
312
+ fullContent,
313
+ temporalBoost
314
+ });
315
+ }
316
+ /**
317
+ * Convert qmd results to ClawVault SearchResult format
318
+ */
319
+ convertResults(qmdResults, options) {
320
+ const { limit = 10, minScore = 0, category, tags, fullContent = false, temporalBoost = false } = options;
321
+ const results = [];
322
+ const maxScore = qmdResults[0]?.score || 1;
323
+ for (const qr of qmdResults) {
324
+ const filePath = this.qmdUriToPath(qr.file);
325
+ const relativePath = this.vaultPath ? path.relative(this.vaultPath, filePath) : filePath;
326
+ const normalizedRelativePath = relativePath.replace(/\\/g, "/");
327
+ if (normalizedRelativePath.startsWith("ledger/archive/") || normalizedRelativePath.includes("/ledger/archive/")) {
328
+ continue;
329
+ }
330
+ const docId = normalizedRelativePath.replace(/\.md$/, "");
331
+ let doc = this.documents.get(docId) ?? this.documents.get(docId.split("/").join(path.sep));
332
+ const modifiedAt = this.resolveModifiedAt(doc, filePath);
333
+ const parts = normalizedRelativePath.split("/");
334
+ const docCategory = parts.length > 1 ? parts[0] : "root";
335
+ if (category && docCategory !== category) continue;
336
+ if (tags && tags.length > 0 && doc) {
337
+ const docTags = new Set(doc.tags);
338
+ if (!tags.some((t) => docTags.has(t))) continue;
339
+ }
340
+ const normalizedScore = maxScore > 0 ? qr.score / maxScore : 0;
341
+ const finalScore = temporalBoost ? normalizedScore * this.getRecencyFactor(modifiedAt) : normalizedScore;
342
+ if (finalScore < minScore) continue;
343
+ if (!doc) {
344
+ doc = {
345
+ id: docId,
346
+ path: filePath,
347
+ category: docCategory,
348
+ title: qr.title || path.basename(relativePath, ".md"),
349
+ content: "",
350
+ // Content loaded separately if needed
351
+ frontmatter: {},
352
+ links: [],
353
+ tags: [],
354
+ modified: modifiedAt
355
+ };
356
+ }
357
+ results.push({
358
+ document: fullContent ? doc : { ...doc, content: "" },
359
+ score: finalScore,
360
+ snippet: this.cleanSnippet(qr.snippet),
361
+ matchedTerms: []
362
+ // qmd doesn't provide this
363
+ });
364
+ }
365
+ return results.sort((a, b) => b.score - a.score).slice(0, limit);
366
+ }
367
+ resolveModifiedAt(doc, filePath) {
368
+ if (doc) return doc.modified;
369
+ try {
370
+ return fs.statSync(filePath).mtime;
371
+ } catch {
372
+ return /* @__PURE__ */ new Date(0);
373
+ }
374
+ }
375
+ getRecencyFactor(modifiedAt) {
376
+ const ageMs = Math.max(0, Date.now() - modifiedAt.getTime());
377
+ const ageDays = ageMs / (24 * 60 * 60 * 1e3);
378
+ if (ageDays < 1) return 1;
379
+ if (ageDays <= 7) return 0.9;
380
+ return 0.7;
381
+ }
382
+ /**
383
+ * Convert qmd:// URI to file path
384
+ */
385
+ qmdUriToPath(uri) {
386
+ if (uri.startsWith("qmd://")) {
387
+ const withoutScheme = uri.slice(6);
388
+ const slashIndex = withoutScheme.indexOf("/");
389
+ if (slashIndex > -1) {
390
+ const relativePath = withoutScheme.slice(slashIndex + 1);
391
+ const root = this.collectionRoot || this.vaultPath;
392
+ if (root) {
393
+ return path.join(root, relativePath);
394
+ }
395
+ return relativePath;
396
+ }
397
+ }
398
+ return uri;
399
+ }
400
+ /**
401
+ * Clean up qmd snippet format
402
+ */
403
+ cleanSnippet(snippet) {
404
+ if (!snippet) return "";
405
+ return snippet.replace(/@@ [-+]?\d+,?\d* @@ \([^)]+\)/g, "").trim().split("\n").slice(0, 3).join("\n").slice(0, 300);
406
+ }
407
+ /**
408
+ * Get all cached documents
409
+ */
410
+ getAllDocuments() {
411
+ return [...this.documents.values()];
412
+ }
413
+ /**
414
+ * Get document count
415
+ */
416
+ get size() {
417
+ return this.documents.size;
418
+ }
419
+ /**
420
+ * Clear the local document cache
421
+ */
422
+ clear() {
423
+ this.documents.clear();
424
+ }
425
+ /**
426
+ * Export documents for persistence
427
+ */
428
+ export() {
429
+ return {
430
+ documents: [...this.documents.values()]
431
+ };
432
+ }
433
+ /**
434
+ * Import from persisted data
435
+ */
436
+ import(data) {
437
+ this.clear();
438
+ for (const doc of data.documents) {
439
+ this.addDocument(doc);
440
+ }
441
+ }
442
+ };
443
+ function extractWikiLinks(content) {
444
+ const matches = content.match(/\[\[([^\]]+)\]\]/g) || [];
445
+ return matches.map((m) => m.slice(2, -2).toLowerCase());
446
+ }
447
+ function extractTags(content) {
448
+ const matches = content.match(/#[\w-]+/g) || [];
449
+ return [...new Set(matches.map((m) => m.slice(1).toLowerCase()))];
450
+ }
451
+
452
+ export {
453
+ QMD_INSTALL_URL,
454
+ QMD_INSTALL_COMMAND,
455
+ QmdUnavailableError,
456
+ getQmdErrorDetails,
457
+ QmdConfigurationError,
458
+ withQmdIndexArgs,
459
+ hasQmd,
460
+ qmdUpdate,
461
+ qmdEmbed,
462
+ SearchEngine,
463
+ extractWikiLinks,
464
+ extractTags
465
+ };
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  listTasks,
3
3
  slugify
4
- } from "./chunk-N2AXRYLC.js";
4
+ } from "./chunk-QWQ3TIKS.js";
5
5
  import {
6
6
  loadSchemaTemplateDefinition,
7
7
  renderDocumentFromTemplate
8
- } from "./chunk-LYHGEHXG.js";
8
+ } from "./chunk-MFAWT5O5.js";
9
9
 
10
10
  // src/lib/project-utils.ts
11
11
  import * as fs from "fs";