clawvault 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.md +54 -14
  2. package/bin/clawvault.js +0 -2
  3. package/bin/command-registration.test.js +13 -1
  4. package/bin/help-contract.test.js +14 -0
  5. package/bin/register-core-commands.js +88 -0
  6. package/bin/register-core-commands.test.js +80 -0
  7. package/bin/register-maintenance-commands.js +57 -6
  8. package/bin/register-query-commands.js +10 -28
  9. package/bin/test-helpers/cli-command-fixtures.js +1 -0
  10. package/dist/chunk-2PKBIKDH.js +130 -0
  11. package/dist/{chunk-2JQ3O2YL.js → chunk-5EFSWZO6.js} +3 -3
  12. package/dist/{chunk-77Q5CSPJ.js → chunk-7SWP5FKU.js} +33 -701
  13. package/dist/{chunk-URXDAUVH.js → chunk-AXSJIFOJ.js} +174 -1
  14. package/dist/{chunk-23YDQ3QU.js → chunk-BLQXXX7Q.js} +6 -6
  15. package/dist/chunk-CSHO3PJB.js +684 -0
  16. package/dist/{chunk-SLXOR3CC.js → chunk-DOIUYIXV.js} +2 -2
  17. package/dist/{chunk-NCKFNBHJ.js → chunk-DVOUSOR3.js} +79 -5
  18. package/dist/{chunk-CLJTREDS.js → chunk-ECGJYWNA.js} +193 -41
  19. package/dist/{chunk-BUEW6IIK.js → chunk-EL6UBSX5.js} +5 -5
  20. package/dist/{chunk-6FH3IULF.js → chunk-FZ5I2NF7.js} +1 -1
  21. package/dist/{chunk-ZN54U2OZ.js → chunk-GFCHWMGD.js} +3 -3
  22. package/dist/{chunk-GNJL4YGR.js → chunk-GJO3CFUN.js} +30 -6
  23. package/dist/chunk-H3JZIB5O.js +322 -0
  24. package/dist/chunk-HEHO7SMV.js +51 -0
  25. package/dist/{chunk-STCQGCEQ.js → chunk-HGDDW24U.js} +3 -3
  26. package/dist/chunk-J3YUXVID.js +907 -0
  27. package/dist/{chunk-Y6VJKXGL.js → chunk-KCYWJDDW.js} +1 -1
  28. package/dist/{chunk-W4SPAEE7.js → chunk-OFOCU2V4.js} +5 -4
  29. package/dist/chunk-PTWPPVC7.js +972 -0
  30. package/dist/{chunk-QSHD36LH.js → chunk-QFWERBDP.js} +2 -2
  31. package/dist/{chunk-QSRRMEYM.js → chunk-S7N7HI5E.js} +1 -1
  32. package/dist/{chunk-PBACDKKP.js → chunk-T7E764W3.js} +3 -3
  33. package/dist/chunk-TDWFBDAQ.js +1016 -0
  34. package/dist/{chunk-ESVS6K2B.js → chunk-TWMI3SNN.js} +6 -5
  35. package/dist/{chunk-2RAZ4ZFE.js → chunk-VBILES4B.js} +1 -1
  36. package/dist/{chunk-ESFLMDRB.js → chunk-VXAGOLDP.js} +3 -3
  37. package/dist/chunk-YCUVAOFC.js +158 -0
  38. package/dist/{chunk-SS4B7P7V.js → chunk-YIDV4VV2.js} +1 -1
  39. package/dist/chunk-ZKWPCBYT.js +600 -0
  40. package/dist/cli/index.js +24 -24
  41. package/dist/commands/archive.js +2 -2
  42. package/dist/commands/benchmark.d.ts +12 -0
  43. package/dist/commands/benchmark.js +12 -0
  44. package/dist/commands/context.js +6 -5
  45. package/dist/commands/doctor.d.ts +8 -3
  46. package/dist/commands/doctor.js +6 -20
  47. package/dist/commands/embed.js +5 -4
  48. package/dist/commands/entities.js +1 -1
  49. package/dist/commands/graph.js +2 -2
  50. package/dist/commands/inbox.d.ts +23 -0
  51. package/dist/commands/inbox.js +11 -0
  52. package/dist/commands/inject.d.ts +1 -1
  53. package/dist/commands/inject.js +3 -3
  54. package/dist/commands/link.js +6 -6
  55. package/dist/commands/maintain.d.ts +32 -0
  56. package/dist/commands/maintain.js +12 -0
  57. package/dist/commands/migrate-observations.js +2 -2
  58. package/dist/commands/observe.js +9 -8
  59. package/dist/commands/rebuild-embeddings.js +47 -16
  60. package/dist/commands/rebuild.js +7 -6
  61. package/dist/commands/reflect.js +5 -5
  62. package/dist/commands/replay.js +8 -7
  63. package/dist/commands/setup.js +3 -2
  64. package/dist/commands/sleep.d.ts +1 -1
  65. package/dist/commands/sleep.js +17 -15
  66. package/dist/commands/status.js +26 -24
  67. package/dist/commands/sync-bd.js +2 -2
  68. package/dist/commands/tailscale.js +2 -2
  69. package/dist/commands/wake.d.ts +1 -1
  70. package/dist/commands/wake.js +8 -7
  71. package/dist/index.d.ts +168 -16
  72. package/dist/index.js +271 -108
  73. package/dist/{inject-DYUrDqQO.d.ts → inject-DEb_jpLi.d.ts} +3 -1
  74. package/dist/lib/config.js +1 -1
  75. package/dist/{types-BbWJoC1c.d.ts → types-DslKvCaj.d.ts} +51 -1
  76. package/hooks/clawvault/HOOK.md +22 -5
  77. package/hooks/clawvault/handler.js +213 -78
  78. package/hooks/clawvault/handler.test.js +109 -43
  79. package/hooks/clawvault/integrity.js +112 -0
  80. package/hooks/clawvault/integrity.test.js +32 -0
  81. package/hooks/clawvault/openclaw.plugin.json +133 -15
  82. package/openclaw.plugin.json +126 -20
  83. package/package.json +2 -2
  84. package/bin/register-workgraph-commands.js +0 -1368
  85. package/dist/chunk-33VSQP4J.js +0 -37
  86. package/dist/chunk-4BQTQMJP.js +0 -93
  87. package/dist/chunk-EK6S23ZB.js +0 -469
  88. package/dist/chunk-GAOWA7GR.js +0 -501
  89. package/dist/chunk-GGA32J2R.js +0 -784
  90. package/dist/chunk-MM6QGW3P.js +0 -207
  91. package/dist/chunk-QVEERJSP.js +0 -152
  92. package/dist/chunk-U4O6C46S.js +0 -154
  93. package/dist/chunk-VSL7KY3M.js +0 -189
  94. package/dist/chunk-WMGIIABP.js +0 -15
  95. package/dist/commands/workgraph.d.ts +0 -124
  96. package/dist/commands/workgraph.js +0 -38
  97. package/dist/ledger-B7g7jhqG.d.ts +0 -44
  98. package/dist/registry-BR4326o0.d.ts +0 -30
  99. package/dist/store-CA-6sKCJ.d.ts +0 -34
  100. package/dist/thread-B9LhXNU0.d.ts +0 -41
  101. package/dist/workgraph/index.d.ts +0 -5
  102. package/dist/workgraph/index.js +0 -23
  103. package/dist/workgraph/ledger.d.ts +0 -2
  104. package/dist/workgraph/ledger.js +0 -25
  105. package/dist/workgraph/registry.d.ts +0 -2
  106. package/dist/workgraph/registry.js +0 -19
  107. package/dist/workgraph/store.d.ts +0 -2
  108. package/dist/workgraph/store.js +0 -25
  109. package/dist/workgraph/thread.d.ts +0 -2
  110. package/dist/workgraph/thread.js +0 -25
  111. package/dist/workgraph/types.d.ts +0 -54
  112. package/dist/workgraph/types.js +0 -7
@@ -1,37 +0,0 @@
1
- import {
2
- registerTailscaleCommands
3
- } from "./chunk-Y6VJKXGL.js";
4
- import {
5
- registerWorkgraphCommands
6
- } from "./chunk-GGA32J2R.js";
7
- import {
8
- registerObserveCommand
9
- } from "./chunk-23YDQ3QU.js";
10
- import {
11
- registerReflectCommand
12
- } from "./chunk-SLXOR3CC.js";
13
- import {
14
- registerContextCommand
15
- } from "./chunk-ZN54U2OZ.js";
16
- import {
17
- registerEmbedCommand
18
- } from "./chunk-PBACDKKP.js";
19
- import {
20
- registerInjectCommand
21
- } from "./chunk-W4SPAEE7.js";
22
-
23
- // src/cli/index.ts
24
- function registerCliCommands(program) {
25
- registerContextCommand(program);
26
- registerInjectCommand(program);
27
- registerObserveCommand(program);
28
- registerReflectCommand(program);
29
- registerEmbedCommand(program);
30
- registerTailscaleCommands(program);
31
- registerWorkgraphCommands(program);
32
- return program;
33
- }
34
-
35
- export {
36
- registerCliCommands
37
- };
@@ -1,93 +0,0 @@
1
- import {
2
- __export
3
- } from "./chunk-2ZDO52B4.js";
4
-
5
- // src/workgraph/ledger.ts
6
- var ledger_exports = {};
7
- __export(ledger_exports, {
8
- activityOf: () => activityOf,
9
- allClaims: () => allClaims,
10
- append: () => append,
11
- currentOwner: () => currentOwner,
12
- historyOf: () => historyOf,
13
- isClaimed: () => isClaimed,
14
- ledgerPath: () => ledgerPath,
15
- readAll: () => readAll,
16
- readSince: () => readSince,
17
- recent: () => recent
18
- });
19
- import fs from "fs";
20
- import path from "path";
21
- var LEDGER_FILE = ".clawvault/ledger.jsonl";
22
- function ledgerPath(vaultPath) {
23
- return path.join(vaultPath, LEDGER_FILE);
24
- }
25
- function append(vaultPath, actor, op, target, type, data) {
26
- const entry = {
27
- ts: (/* @__PURE__ */ new Date()).toISOString(),
28
- actor,
29
- op,
30
- target,
31
- ...type ? { type } : {},
32
- ...data && Object.keys(data).length > 0 ? { data } : {}
33
- };
34
- const lPath = ledgerPath(vaultPath);
35
- const dir = path.dirname(lPath);
36
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
37
- fs.appendFileSync(lPath, JSON.stringify(entry) + "\n", "utf-8");
38
- return entry;
39
- }
40
- function readAll(vaultPath) {
41
- const lPath = ledgerPath(vaultPath);
42
- if (!fs.existsSync(lPath)) return [];
43
- const lines = fs.readFileSync(lPath, "utf-8").split("\n").filter(Boolean);
44
- return lines.map((line) => JSON.parse(line));
45
- }
46
- function readSince(vaultPath, since) {
47
- return readAll(vaultPath).filter((e) => e.ts >= since);
48
- }
49
- function currentOwner(vaultPath, target) {
50
- const entries = readAll(vaultPath).filter((e) => e.target === target);
51
- let owner = null;
52
- for (const e of entries) {
53
- if (e.op === "claim") owner = e.actor;
54
- if (e.op === "release" || e.op === "done" || e.op === "cancel") owner = null;
55
- }
56
- return owner;
57
- }
58
- function isClaimed(vaultPath, target) {
59
- return currentOwner(vaultPath, target) !== null;
60
- }
61
- function historyOf(vaultPath, target) {
62
- return readAll(vaultPath).filter((e) => e.target === target);
63
- }
64
- function activityOf(vaultPath, actor) {
65
- return readAll(vaultPath).filter((e) => e.actor === actor);
66
- }
67
- function allClaims(vaultPath) {
68
- const claims = /* @__PURE__ */ new Map();
69
- const entries = readAll(vaultPath);
70
- for (const e of entries) {
71
- if (e.op === "claim") claims.set(e.target, e.actor);
72
- if (e.op === "release" || e.op === "done" || e.op === "cancel") claims.delete(e.target);
73
- }
74
- return claims;
75
- }
76
- function recent(vaultPath, count = 20) {
77
- const all = readAll(vaultPath);
78
- return all.slice(-count);
79
- }
80
-
81
- export {
82
- ledgerPath,
83
- append,
84
- readAll,
85
- readSince,
86
- currentOwner,
87
- isClaimed,
88
- historyOf,
89
- activityOf,
90
- allClaims,
91
- recent,
92
- ledger_exports
93
- };
@@ -1,469 +0,0 @@
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
- shell: process.platform === "win32"
171
- });
172
- return parseQmdOutput(result);
173
- } catch (err) {
174
- if (err?.code === "ENOENT") {
175
- throw new QmdUnavailableError("NOT_INSTALLED");
176
- }
177
- if (err?.status === 1 && err?.stdout) {
178
- return parseQmdOutput(err.stdout);
179
- }
180
- const output = [err?.stdout, err?.stderr].filter(Boolean).join("\n");
181
- const detectedError = detectQmdError(output, finalArgs);
182
- if (detectedError) {
183
- throw detectedError;
184
- }
185
- if (output) {
186
- try {
187
- return parseQmdOutput(output);
188
- } catch {
189
- }
190
- if (output.includes("collection not found") || output.includes("no such collection")) {
191
- throw new QmdUnavailableError("COLLECTION_NOT_FOUND", output.trim());
192
- }
193
- }
194
- const errorDetail = err?.message || "unknown error";
195
- throw new QmdUnavailableError("EXECUTION_FAILED", errorDetail);
196
- }
197
- }
198
- function hasQmd() {
199
- const result = spawnSync("qmd", ["--version"], { stdio: "ignore", shell: process.platform === "win32" });
200
- return !result.error && (result.status === 0 || result.status === 1);
201
- }
202
- function qmdUpdate(collection, indexName) {
203
- ensureQmdAvailable();
204
- const args = ["update"];
205
- if (collection) {
206
- args.push("-c", collection);
207
- }
208
- execFileSync("qmd", withQmdIndexArgs(args, indexName), { stdio: "inherit", shell: process.platform === "win32" });
209
- }
210
- function qmdEmbed(collection, indexName) {
211
- ensureQmdAvailable();
212
- const args = ["embed"];
213
- if (collection) {
214
- args.push("-c", collection);
215
- }
216
- execFileSync("qmd", withQmdIndexArgs(args, indexName), { stdio: "inherit", shell: process.platform === "win32" });
217
- }
218
- var SearchEngine = class {
219
- documents = /* @__PURE__ */ new Map();
220
- collection = "";
221
- vaultPath = "";
222
- collectionRoot = "";
223
- qmdIndexName;
224
- /**
225
- * Set the collection name (usually vault name)
226
- */
227
- setCollection(name) {
228
- this.collection = name;
229
- }
230
- /**
231
- * Get the current collection name
232
- */
233
- getCollection() {
234
- return this.collection;
235
- }
236
- /**
237
- * Set the vault path for file resolution
238
- */
239
- setVaultPath(vaultPath) {
240
- this.vaultPath = vaultPath;
241
- }
242
- /**
243
- * Set the collection root for qmd:// URI resolution
244
- */
245
- setCollectionRoot(root) {
246
- this.collectionRoot = path.resolve(root);
247
- }
248
- /**
249
- * Set qmd index name (defaults to qmd global default when omitted)
250
- */
251
- setIndexName(indexName) {
252
- this.qmdIndexName = indexName;
253
- }
254
- /**
255
- * Add or update a document in the local cache
256
- * Note: qmd indexing happens via qmd update command
257
- */
258
- addDocument(doc) {
259
- this.documents.set(doc.id, doc);
260
- }
261
- /**
262
- * Remove a document from the local cache
263
- */
264
- removeDocument(id) {
265
- this.documents.delete(id);
266
- }
267
- /**
268
- * No-op for qmd - indexing is managed externally
269
- */
270
- rebuildIDF() {
271
- }
272
- /**
273
- * BM25 search via qmd
274
- */
275
- search(query, options = {}) {
276
- return this.runQmdQuery("search", query, options);
277
- }
278
- /**
279
- * Vector/semantic search via qmd vsearch
280
- */
281
- vsearch(query, options = {}) {
282
- return this.runQmdQuery("vsearch", query, options);
283
- }
284
- /**
285
- * Combined search with query expansion (qmd query command)
286
- */
287
- query(query, options = {}) {
288
- return this.runQmdQuery("query", query, options);
289
- }
290
- runQmdQuery(command, query, options) {
291
- const {
292
- limit = 10,
293
- minScore = 0,
294
- category,
295
- tags,
296
- fullContent = false,
297
- temporalBoost = false
298
- } = options;
299
- if (!query.trim()) return [];
300
- const args = [
301
- command,
302
- query,
303
- "-n",
304
- String(limit * 2),
305
- "--json"
306
- ];
307
- if (this.collection) {
308
- args.push("-c", this.collection);
309
- }
310
- const qmdResults = execQmd(args, this.qmdIndexName);
311
- return this.convertResults(qmdResults, {
312
- limit,
313
- minScore,
314
- category,
315
- tags,
316
- fullContent,
317
- temporalBoost
318
- });
319
- }
320
- /**
321
- * Convert qmd results to ClawVault SearchResult format
322
- */
323
- convertResults(qmdResults, options) {
324
- const { limit = 10, minScore = 0, category, tags, fullContent = false, temporalBoost = false } = options;
325
- const results = [];
326
- const maxScore = qmdResults[0]?.score || 1;
327
- for (const qr of qmdResults) {
328
- const filePath = this.qmdUriToPath(qr.file);
329
- const relativePath = this.vaultPath ? path.relative(this.vaultPath, filePath) : filePath;
330
- const normalizedRelativePath = relativePath.replace(/\\/g, "/");
331
- if (normalizedRelativePath.startsWith("ledger/archive/") || normalizedRelativePath.includes("/ledger/archive/")) {
332
- continue;
333
- }
334
- const docId = normalizedRelativePath.replace(/\.md$/, "");
335
- let doc = this.documents.get(docId) ?? this.documents.get(docId.split("/").join(path.sep));
336
- const modifiedAt = this.resolveModifiedAt(doc, filePath);
337
- const parts = normalizedRelativePath.split("/");
338
- const docCategory = parts.length > 1 ? parts[0] : "root";
339
- if (category && docCategory !== category) continue;
340
- if (tags && tags.length > 0 && doc) {
341
- const docTags = new Set(doc.tags);
342
- if (!tags.some((t) => docTags.has(t))) continue;
343
- }
344
- const normalizedScore = maxScore > 0 ? qr.score / maxScore : 0;
345
- const finalScore = temporalBoost ? normalizedScore * this.getRecencyFactor(modifiedAt) : normalizedScore;
346
- if (finalScore < minScore) continue;
347
- if (!doc) {
348
- doc = {
349
- id: docId,
350
- path: filePath,
351
- category: docCategory,
352
- title: qr.title || path.basename(relativePath, ".md"),
353
- content: "",
354
- // Content loaded separately if needed
355
- frontmatter: {},
356
- links: [],
357
- tags: [],
358
- modified: modifiedAt
359
- };
360
- }
361
- results.push({
362
- document: fullContent ? doc : { ...doc, content: "" },
363
- score: finalScore,
364
- snippet: this.cleanSnippet(qr.snippet),
365
- matchedTerms: []
366
- // qmd doesn't provide this
367
- });
368
- }
369
- return results.sort((a, b) => b.score - a.score).slice(0, limit);
370
- }
371
- resolveModifiedAt(doc, filePath) {
372
- if (doc) return doc.modified;
373
- try {
374
- return fs.statSync(filePath).mtime;
375
- } catch {
376
- return /* @__PURE__ */ new Date(0);
377
- }
378
- }
379
- getRecencyFactor(modifiedAt) {
380
- const ageMs = Math.max(0, Date.now() - modifiedAt.getTime());
381
- const ageDays = ageMs / (24 * 60 * 60 * 1e3);
382
- if (ageDays < 1) return 1;
383
- if (ageDays <= 7) return 0.9;
384
- return 0.7;
385
- }
386
- /**
387
- * Convert qmd:// URI to file path
388
- */
389
- qmdUriToPath(uri) {
390
- if (uri.startsWith("qmd://")) {
391
- const withoutScheme = uri.slice(6);
392
- const slashIndex = withoutScheme.indexOf("/");
393
- if (slashIndex > -1) {
394
- const relativePath = withoutScheme.slice(slashIndex + 1);
395
- const root = this.collectionRoot || this.vaultPath;
396
- if (root) {
397
- return path.join(root, relativePath);
398
- }
399
- return relativePath;
400
- }
401
- }
402
- return uri;
403
- }
404
- /**
405
- * Clean up qmd snippet format
406
- */
407
- cleanSnippet(snippet) {
408
- if (!snippet) return "";
409
- return snippet.replace(/@@ [-+]?\d+,?\d* @@ \([^)]+\)/g, "").trim().split("\n").slice(0, 3).join("\n").slice(0, 300);
410
- }
411
- /**
412
- * Get all cached documents
413
- */
414
- getAllDocuments() {
415
- return [...this.documents.values()];
416
- }
417
- /**
418
- * Get document count
419
- */
420
- get size() {
421
- return this.documents.size;
422
- }
423
- /**
424
- * Clear the local document cache
425
- */
426
- clear() {
427
- this.documents.clear();
428
- }
429
- /**
430
- * Export documents for persistence
431
- */
432
- export() {
433
- return {
434
- documents: [...this.documents.values()]
435
- };
436
- }
437
- /**
438
- * Import from persisted data
439
- */
440
- import(data) {
441
- this.clear();
442
- for (const doc of data.documents) {
443
- this.addDocument(doc);
444
- }
445
- }
446
- };
447
- function extractWikiLinks(content) {
448
- const matches = content.match(/\[\[([^\]]+)\]\]/g) || [];
449
- return matches.map((m) => m.slice(2, -2).toLowerCase());
450
- }
451
- function extractTags(content) {
452
- const matches = content.match(/#[\w-]+/g) || [];
453
- return [...new Set(matches.map((m) => m.slice(1).toLowerCase()))];
454
- }
455
-
456
- export {
457
- QMD_INSTALL_URL,
458
- QMD_INSTALL_COMMAND,
459
- QmdUnavailableError,
460
- getQmdErrorDetails,
461
- QmdConfigurationError,
462
- withQmdIndexArgs,
463
- hasQmd,
464
- qmdUpdate,
465
- qmdEmbed,
466
- SearchEngine,
467
- extractWikiLinks,
468
- extractTags
469
- };