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.
- package/README.md +54 -14
- package/bin/clawvault.js +0 -2
- package/bin/command-registration.test.js +13 -1
- package/bin/help-contract.test.js +14 -0
- package/bin/register-core-commands.js +88 -0
- package/bin/register-core-commands.test.js +80 -0
- package/bin/register-maintenance-commands.js +57 -6
- package/bin/register-query-commands.js +10 -28
- package/bin/test-helpers/cli-command-fixtures.js +1 -0
- package/dist/chunk-2PKBIKDH.js +130 -0
- package/dist/{chunk-2JQ3O2YL.js → chunk-5EFSWZO6.js} +3 -3
- package/dist/{chunk-77Q5CSPJ.js → chunk-7SWP5FKU.js} +33 -701
- package/dist/{chunk-URXDAUVH.js → chunk-AXSJIFOJ.js} +174 -1
- package/dist/{chunk-23YDQ3QU.js → chunk-BLQXXX7Q.js} +6 -6
- package/dist/chunk-CSHO3PJB.js +684 -0
- package/dist/{chunk-SLXOR3CC.js → chunk-DOIUYIXV.js} +2 -2
- package/dist/{chunk-NCKFNBHJ.js → chunk-DVOUSOR3.js} +79 -5
- package/dist/{chunk-CLJTREDS.js → chunk-ECGJYWNA.js} +193 -41
- package/dist/{chunk-BUEW6IIK.js → chunk-EL6UBSX5.js} +5 -5
- package/dist/{chunk-6FH3IULF.js → chunk-FZ5I2NF7.js} +1 -1
- package/dist/{chunk-ZN54U2OZ.js → chunk-GFCHWMGD.js} +3 -3
- package/dist/{chunk-GNJL4YGR.js → chunk-GJO3CFUN.js} +30 -6
- package/dist/chunk-H3JZIB5O.js +322 -0
- package/dist/chunk-HEHO7SMV.js +51 -0
- package/dist/{chunk-STCQGCEQ.js → chunk-HGDDW24U.js} +3 -3
- package/dist/chunk-J3YUXVID.js +907 -0
- package/dist/{chunk-Y6VJKXGL.js → chunk-KCYWJDDW.js} +1 -1
- package/dist/{chunk-W4SPAEE7.js → chunk-OFOCU2V4.js} +5 -4
- package/dist/chunk-PTWPPVC7.js +972 -0
- package/dist/{chunk-QSHD36LH.js → chunk-QFWERBDP.js} +2 -2
- package/dist/{chunk-QSRRMEYM.js → chunk-S7N7HI5E.js} +1 -1
- package/dist/{chunk-PBACDKKP.js → chunk-T7E764W3.js} +3 -3
- package/dist/chunk-TDWFBDAQ.js +1016 -0
- package/dist/{chunk-ESVS6K2B.js → chunk-TWMI3SNN.js} +6 -5
- package/dist/{chunk-2RAZ4ZFE.js → chunk-VBILES4B.js} +1 -1
- package/dist/{chunk-ESFLMDRB.js → chunk-VXAGOLDP.js} +3 -3
- package/dist/chunk-YCUVAOFC.js +158 -0
- package/dist/{chunk-SS4B7P7V.js → chunk-YIDV4VV2.js} +1 -1
- package/dist/chunk-ZKWPCBYT.js +600 -0
- package/dist/cli/index.js +24 -24
- package/dist/commands/archive.js +2 -2
- package/dist/commands/benchmark.d.ts +12 -0
- package/dist/commands/benchmark.js +12 -0
- package/dist/commands/context.js +6 -5
- package/dist/commands/doctor.d.ts +8 -3
- package/dist/commands/doctor.js +6 -20
- package/dist/commands/embed.js +5 -4
- package/dist/commands/entities.js +1 -1
- package/dist/commands/graph.js +2 -2
- package/dist/commands/inbox.d.ts +23 -0
- package/dist/commands/inbox.js +11 -0
- package/dist/commands/inject.d.ts +1 -1
- package/dist/commands/inject.js +3 -3
- package/dist/commands/link.js +6 -6
- package/dist/commands/maintain.d.ts +32 -0
- package/dist/commands/maintain.js +12 -0
- package/dist/commands/migrate-observations.js +2 -2
- package/dist/commands/observe.js +9 -8
- package/dist/commands/rebuild-embeddings.js +47 -16
- package/dist/commands/rebuild.js +7 -6
- package/dist/commands/reflect.js +5 -5
- package/dist/commands/replay.js +8 -7
- package/dist/commands/setup.js +3 -2
- package/dist/commands/sleep.d.ts +1 -1
- package/dist/commands/sleep.js +17 -15
- package/dist/commands/status.js +26 -24
- package/dist/commands/sync-bd.js +2 -2
- package/dist/commands/tailscale.js +2 -2
- package/dist/commands/wake.d.ts +1 -1
- package/dist/commands/wake.js +8 -7
- package/dist/index.d.ts +168 -16
- package/dist/index.js +271 -108
- package/dist/{inject-DYUrDqQO.d.ts → inject-DEb_jpLi.d.ts} +3 -1
- package/dist/lib/config.js +1 -1
- package/dist/{types-BbWJoC1c.d.ts → types-DslKvCaj.d.ts} +51 -1
- package/hooks/clawvault/HOOK.md +22 -5
- package/hooks/clawvault/handler.js +213 -78
- package/hooks/clawvault/handler.test.js +109 -43
- package/hooks/clawvault/integrity.js +112 -0
- package/hooks/clawvault/integrity.test.js +32 -0
- package/hooks/clawvault/openclaw.plugin.json +133 -15
- package/openclaw.plugin.json +126 -20
- package/package.json +2 -2
- package/bin/register-workgraph-commands.js +0 -1368
- package/dist/chunk-33VSQP4J.js +0 -37
- package/dist/chunk-4BQTQMJP.js +0 -93
- package/dist/chunk-EK6S23ZB.js +0 -469
- package/dist/chunk-GAOWA7GR.js +0 -501
- package/dist/chunk-GGA32J2R.js +0 -784
- package/dist/chunk-MM6QGW3P.js +0 -207
- package/dist/chunk-QVEERJSP.js +0 -152
- package/dist/chunk-U4O6C46S.js +0 -154
- package/dist/chunk-VSL7KY3M.js +0 -189
- package/dist/chunk-WMGIIABP.js +0 -15
- package/dist/commands/workgraph.d.ts +0 -124
- package/dist/commands/workgraph.js +0 -38
- package/dist/ledger-B7g7jhqG.d.ts +0 -44
- package/dist/registry-BR4326o0.d.ts +0 -30
- package/dist/store-CA-6sKCJ.d.ts +0 -34
- package/dist/thread-B9LhXNU0.d.ts +0 -41
- package/dist/workgraph/index.d.ts +0 -5
- package/dist/workgraph/index.js +0 -23
- package/dist/workgraph/ledger.d.ts +0 -2
- package/dist/workgraph/ledger.js +0 -25
- package/dist/workgraph/registry.d.ts +0 -2
- package/dist/workgraph/registry.js +0 -19
- package/dist/workgraph/store.d.ts +0 -2
- package/dist/workgraph/store.js +0 -25
- package/dist/workgraph/thread.d.ts +0 -2
- package/dist/workgraph/thread.js +0 -25
- package/dist/workgraph/types.d.ts +0 -54
- package/dist/workgraph/types.js +0 -7
|
@@ -10,6 +10,8 @@ var DEFAULT_MODELS = {
|
|
|
10
10
|
openclaw: "gpt-4o-mini"
|
|
11
11
|
};
|
|
12
12
|
var XAI_BASE_URL = "https://api.x.ai/v1";
|
|
13
|
+
var VAULT_CONFIG_FILE = ".clawvault.json";
|
|
14
|
+
var LLM_MODEL_TIERS = ["background", "default", "complex"];
|
|
13
15
|
function resolveOpenClawHome() {
|
|
14
16
|
return process.env.OPENCLAW_HOME?.trim() || path.join(os.homedir(), ".openclaw");
|
|
15
17
|
}
|
|
@@ -88,6 +90,78 @@ function resolveLlmProvider() {
|
|
|
88
90
|
}
|
|
89
91
|
return null;
|
|
90
92
|
}
|
|
93
|
+
function resolveNearestVaultPath(startPath) {
|
|
94
|
+
let current = path.resolve(startPath);
|
|
95
|
+
while (true) {
|
|
96
|
+
if (fs.existsSync(path.join(current, VAULT_CONFIG_FILE))) {
|
|
97
|
+
return current;
|
|
98
|
+
}
|
|
99
|
+
const parent = path.dirname(current);
|
|
100
|
+
if (parent === current) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
current = parent;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function resolveVaultPathForModelConfig() {
|
|
107
|
+
const configuredVaultPath = process.env.CLAWVAULT_PATH?.trim();
|
|
108
|
+
if (configuredVaultPath) {
|
|
109
|
+
return path.resolve(configuredVaultPath);
|
|
110
|
+
}
|
|
111
|
+
return resolveNearestVaultPath(process.cwd());
|
|
112
|
+
}
|
|
113
|
+
function readTieredModelConfig() {
|
|
114
|
+
const vaultPath = resolveVaultPathForModelConfig();
|
|
115
|
+
if (!vaultPath) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const configPath = path.join(vaultPath, VAULT_CONFIG_FILE);
|
|
119
|
+
if (!fs.existsSync(configPath)) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
const parsed = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
124
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const models = parsed.models;
|
|
128
|
+
if (!models || typeof models !== "object" || Array.isArray(models)) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const normalized = {};
|
|
132
|
+
for (const tier of LLM_MODEL_TIERS) {
|
|
133
|
+
const candidate = models[tier];
|
|
134
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
135
|
+
normalized[tier] = candidate.trim();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return Object.keys(normalized).length > 0 ? normalized : null;
|
|
139
|
+
} catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function resolveTieredModel(tier) {
|
|
144
|
+
const tieredConfig = readTieredModelConfig();
|
|
145
|
+
if (!tieredConfig) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
return tieredConfig[tier] ?? tieredConfig.default ?? null;
|
|
149
|
+
}
|
|
150
|
+
function resolveRequestModel(options, fallbackModel) {
|
|
151
|
+
if (typeof options.model === "string" && options.model.trim()) {
|
|
152
|
+
return options.model.trim();
|
|
153
|
+
}
|
|
154
|
+
const tier = options.tier ?? "default";
|
|
155
|
+
const configuredTierModel = resolveTieredModel(tier);
|
|
156
|
+
if (configuredTierModel) {
|
|
157
|
+
return configuredTierModel;
|
|
158
|
+
}
|
|
159
|
+
const envModel = process.env.CLAWVAULT_MODEL?.trim();
|
|
160
|
+
if (envModel) {
|
|
161
|
+
return envModel;
|
|
162
|
+
}
|
|
163
|
+
return fallbackModel;
|
|
164
|
+
}
|
|
91
165
|
async function requestLlmCompletion(options) {
|
|
92
166
|
const provider = options.provider ?? resolveLlmProvider();
|
|
93
167
|
if (!provider) {
|
|
@@ -121,7 +195,7 @@ async function callAnthropic(options, provider) {
|
|
|
121
195
|
"anthropic-version": "2023-06-01"
|
|
122
196
|
},
|
|
123
197
|
body: JSON.stringify({
|
|
124
|
-
model: options
|
|
198
|
+
model: resolveRequestModel(options, DEFAULT_MODELS[provider]),
|
|
125
199
|
temperature: options.temperature ?? 0.1,
|
|
126
200
|
max_tokens: options.maxTokens ?? 1200,
|
|
127
201
|
messages: [{ role: "user", content: options.prompt }]
|
|
@@ -151,7 +225,7 @@ async function callOpenAI(options, provider) {
|
|
|
151
225
|
authorization: `Bearer ${apiKey}`
|
|
152
226
|
},
|
|
153
227
|
body: JSON.stringify({
|
|
154
|
-
model: options
|
|
228
|
+
model: resolveRequestModel(options, DEFAULT_MODELS[provider]),
|
|
155
229
|
temperature: options.temperature ?? 0.1,
|
|
156
230
|
max_tokens: options.maxTokens ?? 1200,
|
|
157
231
|
messages
|
|
@@ -181,7 +255,7 @@ async function callXAI(options, provider) {
|
|
|
181
255
|
authorization: `Bearer ${apiKey}`
|
|
182
256
|
},
|
|
183
257
|
body: JSON.stringify({
|
|
184
|
-
model: options
|
|
258
|
+
model: resolveRequestModel(options, DEFAULT_MODELS[provider]),
|
|
185
259
|
temperature: options.temperature ?? 0.1,
|
|
186
260
|
max_tokens: options.maxTokens ?? 1200,
|
|
187
261
|
messages
|
|
@@ -199,7 +273,7 @@ async function callGemini(options, provider) {
|
|
|
199
273
|
return "";
|
|
200
274
|
}
|
|
201
275
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
202
|
-
const model = options
|
|
276
|
+
const model = resolveRequestModel(options, DEFAULT_MODELS[provider]);
|
|
203
277
|
const response = await fetchImpl(
|
|
204
278
|
`https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent`,
|
|
205
279
|
{
|
|
@@ -238,7 +312,7 @@ async function callOpenClaw(options) {
|
|
|
238
312
|
authorization: `Bearer ${config.apiKey}`
|
|
239
313
|
},
|
|
240
314
|
body: JSON.stringify({
|
|
241
|
-
model: options
|
|
315
|
+
model: resolveRequestModel(options, config.defaultModel),
|
|
242
316
|
temperature: options.temperature ?? 0.1,
|
|
243
317
|
max_tokens: options.maxTokens ?? 1200,
|
|
244
318
|
messages
|
|
@@ -1,20 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_CATEGORIES,
|
|
3
|
+
TYPE_TO_CATEGORY
|
|
4
|
+
} from "./chunk-2CDEETQN.js";
|
|
1
5
|
import {
|
|
2
6
|
loadVaultQmdConfig,
|
|
3
7
|
recoverQmdEmbeddingIfNeeded
|
|
4
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-FZ5I2NF7.js";
|
|
5
9
|
import {
|
|
6
|
-
QmdUnavailableError,
|
|
7
10
|
SearchEngine,
|
|
8
11
|
extractTags,
|
|
9
12
|
extractWikiLinks,
|
|
10
13
|
hasQmd,
|
|
11
14
|
qmdEmbed,
|
|
12
15
|
qmdUpdate
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import {
|
|
15
|
-
DEFAULT_CATEGORIES,
|
|
16
|
-
TYPE_TO_CATEGORY
|
|
17
|
-
} from "./chunk-2CDEETQN.js";
|
|
16
|
+
} from "./chunk-PTWPPVC7.js";
|
|
18
17
|
import {
|
|
19
18
|
buildOrUpdateMemoryGraphIndex
|
|
20
19
|
} from "./chunk-33DOSHTA.js";
|
|
@@ -35,17 +34,16 @@ var ClawVault = class {
|
|
|
35
34
|
if (typeof vaultPath !== "string" || !vaultPath.trim()) {
|
|
36
35
|
throw new Error(`Invalid vault path: expected a non-empty string, received ${typeof vaultPath}`);
|
|
37
36
|
}
|
|
38
|
-
if (!hasQmd()) {
|
|
39
|
-
const error = new QmdUnavailableError("NOT_INSTALLED");
|
|
40
|
-
console.error(error.toUserMessage());
|
|
41
|
-
throw error;
|
|
42
|
-
}
|
|
43
37
|
this.config = {
|
|
44
38
|
path: path.resolve(vaultPath),
|
|
45
39
|
name: path.basename(vaultPath),
|
|
46
40
|
categories: DEFAULT_CATEGORIES,
|
|
47
41
|
qmdCollection: void 0,
|
|
48
|
-
qmdRoot: void 0
|
|
42
|
+
qmdRoot: void 0,
|
|
43
|
+
search: {
|
|
44
|
+
backend: "in-process",
|
|
45
|
+
qmdFallback: true
|
|
46
|
+
}
|
|
49
47
|
};
|
|
50
48
|
this.search = new SearchEngine();
|
|
51
49
|
this.applyQmdConfig();
|
|
@@ -54,11 +52,6 @@ var ClawVault = class {
|
|
|
54
52
|
* Initialize a new vault
|
|
55
53
|
*/
|
|
56
54
|
async init(options = {}, initFlags) {
|
|
57
|
-
if (!hasQmd()) {
|
|
58
|
-
const error = new QmdUnavailableError("NOT_INSTALLED");
|
|
59
|
-
console.error(error.toUserMessage());
|
|
60
|
-
throw error;
|
|
61
|
-
}
|
|
62
55
|
const vaultPath = this.config.path;
|
|
63
56
|
const flags = initFlags || {};
|
|
64
57
|
this.config = { ...this.config, ...options };
|
|
@@ -99,7 +92,8 @@ var ClawVault = class {
|
|
|
99
92
|
categories: this.config.categories,
|
|
100
93
|
documentCount: 0,
|
|
101
94
|
qmdCollection: this.getQmdCollection(),
|
|
102
|
-
qmdRoot: this.getQmdRoot()
|
|
95
|
+
qmdRoot: this.getQmdRoot(),
|
|
96
|
+
search: this.config.search ?? { backend: "in-process", qmdFallback: true }
|
|
103
97
|
};
|
|
104
98
|
fs.writeFileSync(configPath, JSON.stringify(meta, null, 2));
|
|
105
99
|
if (!flags.skipBases && this.config.categories.includes("tasks")) {
|
|
@@ -210,11 +204,6 @@ var ClawVault = class {
|
|
|
210
204
|
* Load an existing vault
|
|
211
205
|
*/
|
|
212
206
|
async load() {
|
|
213
|
-
if (!hasQmd()) {
|
|
214
|
-
const error = new QmdUnavailableError("NOT_INSTALLED");
|
|
215
|
-
console.error(error.toUserMessage());
|
|
216
|
-
throw error;
|
|
217
|
-
}
|
|
218
207
|
const vaultPath = this.config.path;
|
|
219
208
|
const configPath = path.join(vaultPath, CONFIG_FILE);
|
|
220
209
|
if (!fs.existsSync(configPath)) {
|
|
@@ -225,27 +214,30 @@ var ClawVault = class {
|
|
|
225
214
|
this.config.categories = Array.isArray(meta.categories) ? meta.categories : this.config.categories;
|
|
226
215
|
this.config.qmdCollection = typeof meta.qmdCollection === "string" ? meta.qmdCollection : void 0;
|
|
227
216
|
this.config.qmdRoot = typeof meta.qmdRoot === "string" ? meta.qmdRoot : void 0;
|
|
217
|
+
this.config.search = meta.search && typeof meta.search === "object" && !Array.isArray(meta.search) ? meta.search : this.config.search;
|
|
228
218
|
if (!meta.qmdCollection || !meta.qmdRoot) {
|
|
229
219
|
meta.qmdCollection = meta.qmdCollection || meta.name;
|
|
230
220
|
meta.qmdRoot = meta.qmdRoot || this.config.path;
|
|
231
221
|
fs.writeFileSync(configPath, JSON.stringify(meta, null, 2));
|
|
232
222
|
}
|
|
233
223
|
this.applyQmdConfig(meta);
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
224
|
+
if (hasQmd()) {
|
|
225
|
+
try {
|
|
226
|
+
const recovery = recoverQmdEmbeddingIfNeeded({
|
|
227
|
+
vaultPath: this.config.path,
|
|
228
|
+
collection: this.getQmdCollection(),
|
|
229
|
+
rootPath: this.getQmdRoot(),
|
|
230
|
+
mode: "marker-only",
|
|
231
|
+
onLog: (message) => console.warn(`[clawvault] ${message}`)
|
|
232
|
+
});
|
|
233
|
+
if (recovery.recovered) {
|
|
234
|
+
console.warn(`[clawvault] qmd embedding recovery finished for "${this.getQmdCollection()}".`);
|
|
235
|
+
}
|
|
236
|
+
} catch (err) {
|
|
237
|
+
console.warn(
|
|
238
|
+
`[clawvault] qmd embedding recovery failed: ${err?.message || "unknown error"}`
|
|
239
|
+
);
|
|
244
240
|
}
|
|
245
|
-
} catch (err) {
|
|
246
|
-
console.warn(
|
|
247
|
-
`[clawvault] qmd embedding recovery failed: ${err?.message || "unknown error"}`
|
|
248
|
-
);
|
|
249
241
|
}
|
|
250
242
|
await this.reindex();
|
|
251
243
|
this.initialized = true;
|
|
@@ -342,6 +334,28 @@ var ClawVault = class {
|
|
|
342
334
|
}
|
|
343
335
|
return doc;
|
|
344
336
|
}
|
|
337
|
+
/**
|
|
338
|
+
* Patch an existing document and incrementally refresh index state for that file only.
|
|
339
|
+
*/
|
|
340
|
+
async patch(options) {
|
|
341
|
+
const relativePath = this.resolveDocumentRelativePath(options.idOrPath);
|
|
342
|
+
const absolutePath = path.join(this.config.path, relativePath);
|
|
343
|
+
if (!fs.existsSync(absolutePath)) {
|
|
344
|
+
throw new Error(`Document not found: ${options.idOrPath}`);
|
|
345
|
+
}
|
|
346
|
+
const raw = fs.readFileSync(absolutePath, "utf-8");
|
|
347
|
+
const { frontmatter, body } = this.splitFrontmatter(raw);
|
|
348
|
+
const updatedBody = this.applyPatchToBody(body, options);
|
|
349
|
+
if (updatedBody === body) {
|
|
350
|
+
throw new Error("Patch made no changes to the document body.");
|
|
351
|
+
}
|
|
352
|
+
fs.writeFileSync(absolutePath, `${frontmatter}${updatedBody}`);
|
|
353
|
+
const doc = await this.reindexDocument(relativePath);
|
|
354
|
+
if (!doc) {
|
|
355
|
+
throw new Error(`Failed to reload patched document: ${options.idOrPath}`);
|
|
356
|
+
}
|
|
357
|
+
return doc;
|
|
358
|
+
}
|
|
345
359
|
/**
|
|
346
360
|
* Quick store to inbox
|
|
347
361
|
*/
|
|
@@ -354,19 +368,19 @@ var ClawVault = class {
|
|
|
354
368
|
});
|
|
355
369
|
}
|
|
356
370
|
/**
|
|
357
|
-
* Search the vault (
|
|
371
|
+
* Search the vault (in-process hybrid by default, qmd fallback optional)
|
|
358
372
|
*/
|
|
359
373
|
async find(query, options = {}) {
|
|
360
374
|
return this.search.search(query, options);
|
|
361
375
|
}
|
|
362
376
|
/**
|
|
363
|
-
* Semantic/vector search (
|
|
377
|
+
* Semantic/vector search (hosted embeddings, qmd fallback optional)
|
|
364
378
|
*/
|
|
365
379
|
async vsearch(query, options = {}) {
|
|
366
380
|
return this.search.vsearch(query, options);
|
|
367
381
|
}
|
|
368
382
|
/**
|
|
369
|
-
* Combined search
|
|
383
|
+
* Combined search entrypoint (currently aliases hybrid search)
|
|
370
384
|
*/
|
|
371
385
|
async query(query, options = {}) {
|
|
372
386
|
return this.search.query(query, options);
|
|
@@ -743,6 +757,142 @@ var ClawVault = class {
|
|
|
743
757
|
}
|
|
744
758
|
return fallback || (/* @__PURE__ */ new Date()).toISOString();
|
|
745
759
|
}
|
|
760
|
+
async reindexDocument(relativePath) {
|
|
761
|
+
const doc = await this.loadDocument(relativePath);
|
|
762
|
+
if (!doc) return null;
|
|
763
|
+
this.search.addDocument(doc);
|
|
764
|
+
await this.saveIndex();
|
|
765
|
+
await this.syncMemoryGraphIndex();
|
|
766
|
+
return doc;
|
|
767
|
+
}
|
|
768
|
+
resolveDocumentRelativePath(idOrPath) {
|
|
769
|
+
if (typeof idOrPath !== "string" || !idOrPath.trim()) {
|
|
770
|
+
throw new Error("idOrPath is required");
|
|
771
|
+
}
|
|
772
|
+
const trimmed = idOrPath.trim().replace(/^[\\/]+/, "");
|
|
773
|
+
const withExtension = trimmed.endsWith(".md") ? trimmed : `${trimmed}.md`;
|
|
774
|
+
const normalized = path.normalize(withExtension);
|
|
775
|
+
const resolved = path.resolve(this.config.path, normalized);
|
|
776
|
+
const relative2 = path.relative(this.config.path, resolved);
|
|
777
|
+
if (relative2.startsWith("..") || path.isAbsolute(relative2)) {
|
|
778
|
+
throw new Error(`Document path escapes vault: ${idOrPath}`);
|
|
779
|
+
}
|
|
780
|
+
return relative2;
|
|
781
|
+
}
|
|
782
|
+
splitFrontmatter(raw) {
|
|
783
|
+
const match = raw.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
|
|
784
|
+
if (!match) {
|
|
785
|
+
return { frontmatter: "", body: raw };
|
|
786
|
+
}
|
|
787
|
+
const frontmatter = match[0];
|
|
788
|
+
return {
|
|
789
|
+
frontmatter,
|
|
790
|
+
body: raw.slice(frontmatter.length)
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
applyPatchToBody(body, options) {
|
|
794
|
+
if (options.mode === "append") {
|
|
795
|
+
if (typeof options.append !== "string" || options.append.length === 0) {
|
|
796
|
+
throw new Error("Append mode requires non-empty append text.");
|
|
797
|
+
}
|
|
798
|
+
if (options.section) {
|
|
799
|
+
return this.patchMarkdownSection(
|
|
800
|
+
body,
|
|
801
|
+
options.section,
|
|
802
|
+
(sectionBody) => this.appendText(sectionBody, options.append)
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
return this.appendText(body, options.append);
|
|
806
|
+
}
|
|
807
|
+
if (options.mode === "replace") {
|
|
808
|
+
if (typeof options.replace !== "string" || options.replace.length === 0) {
|
|
809
|
+
throw new Error("Replace mode requires non-empty --replace text.");
|
|
810
|
+
}
|
|
811
|
+
if (typeof options.with !== "string") {
|
|
812
|
+
throw new Error("Replace mode requires --with text.");
|
|
813
|
+
}
|
|
814
|
+
if (options.section) {
|
|
815
|
+
return this.patchMarkdownSection(
|
|
816
|
+
body,
|
|
817
|
+
options.section,
|
|
818
|
+
(sectionBody) => this.replaceAllOccurrences(sectionBody, options.replace, options.with, `section "${options.section}"`)
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
return this.replaceAllOccurrences(body, options.replace, options.with, "document");
|
|
822
|
+
}
|
|
823
|
+
if (options.mode === "content") {
|
|
824
|
+
if (typeof options.content !== "string") {
|
|
825
|
+
throw new Error("Content mode requires --content text.");
|
|
826
|
+
}
|
|
827
|
+
if (options.section) {
|
|
828
|
+
return this.patchMarkdownSection(body, options.section, () => options.content);
|
|
829
|
+
}
|
|
830
|
+
return options.content;
|
|
831
|
+
}
|
|
832
|
+
throw new Error(`Unsupported patch mode: ${String(options.mode)}`);
|
|
833
|
+
}
|
|
834
|
+
appendText(existing, addition) {
|
|
835
|
+
if (addition.length === 0) return existing;
|
|
836
|
+
if (existing.length === 0) return addition;
|
|
837
|
+
return existing.endsWith("\n") ? `${existing}${addition}` : `${existing}
|
|
838
|
+
${addition}`;
|
|
839
|
+
}
|
|
840
|
+
replaceAllOccurrences(input, searchText, replacement, scopeLabel) {
|
|
841
|
+
if (!input.includes(searchText)) {
|
|
842
|
+
throw new Error(`No matches found for "${searchText}" in ${scopeLabel}.`);
|
|
843
|
+
}
|
|
844
|
+
return input.split(searchText).join(replacement);
|
|
845
|
+
}
|
|
846
|
+
patchMarkdownSection(markdown, sectionName, applySectionPatch) {
|
|
847
|
+
const lines = markdown.split(/\r?\n/);
|
|
848
|
+
const normalize2 = (value) => value.replace(/^#+\s*/, "").trim().toLowerCase();
|
|
849
|
+
const targetName = normalize2(sectionName);
|
|
850
|
+
let sectionStart = -1;
|
|
851
|
+
let sectionLevel = 0;
|
|
852
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
853
|
+
const line = lines[i];
|
|
854
|
+
const match = line.match(/^(#{1,6})\s+(.*?)\s*$/);
|
|
855
|
+
if (!match) continue;
|
|
856
|
+
const [, marks, heading] = match;
|
|
857
|
+
if (normalize2(heading) === targetName) {
|
|
858
|
+
sectionStart = i;
|
|
859
|
+
sectionLevel = marks.length;
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
if (sectionStart < 0) {
|
|
864
|
+
throw new Error(`Section not found: ${sectionName}`);
|
|
865
|
+
}
|
|
866
|
+
let sectionEnd = lines.length;
|
|
867
|
+
for (let i = sectionStart + 1; i < lines.length; i += 1) {
|
|
868
|
+
const match = lines[i].match(/^(#{1,6})\s+(.*?)\s*$/);
|
|
869
|
+
if (!match) continue;
|
|
870
|
+
if (match[1].length <= sectionLevel) {
|
|
871
|
+
sectionEnd = i;
|
|
872
|
+
break;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
const existingSectionBody = lines.slice(sectionStart + 1, sectionEnd).join("\n");
|
|
876
|
+
const updatedSectionBody = applySectionPatch(existingSectionBody);
|
|
877
|
+
const rebuiltSection = updatedSectionBody.length > 0 ? `${lines[sectionStart]}
|
|
878
|
+
${updatedSectionBody}` : lines[sectionStart];
|
|
879
|
+
const head = lines.slice(0, sectionStart).join("\n");
|
|
880
|
+
const tail = lines.slice(sectionEnd).join("\n");
|
|
881
|
+
if (head.length > 0 && tail.length > 0) {
|
|
882
|
+
return `${head}
|
|
883
|
+
${rebuiltSection}
|
|
884
|
+
${tail}`;
|
|
885
|
+
}
|
|
886
|
+
if (head.length > 0) {
|
|
887
|
+
return `${head}
|
|
888
|
+
${rebuiltSection}`;
|
|
889
|
+
}
|
|
890
|
+
if (tail.length > 0) {
|
|
891
|
+
return `${rebuiltSection}
|
|
892
|
+
${tail}`;
|
|
893
|
+
}
|
|
894
|
+
return rebuiltSection;
|
|
895
|
+
}
|
|
746
896
|
/**
|
|
747
897
|
* Extract the date portion (YYYY-MM-DD) from an ISO date string or Date object.
|
|
748
898
|
* Provides safe handling for various date formats.
|
|
@@ -765,9 +915,11 @@ var ClawVault = class {
|
|
|
765
915
|
}
|
|
766
916
|
this.config.qmdCollection = collection;
|
|
767
917
|
this.config.qmdRoot = root;
|
|
918
|
+
this.config.search = meta?.search && typeof meta.search === "object" && !Array.isArray(meta.search) ? meta.search : this.config.search ?? { backend: "in-process", qmdFallback: true };
|
|
768
919
|
this.search.setVaultPath(this.config.path);
|
|
769
920
|
this.search.setCollection(collection);
|
|
770
921
|
this.search.setCollectionRoot(root);
|
|
922
|
+
this.search.setSearchConfig(this.config.search);
|
|
771
923
|
}
|
|
772
924
|
slugify(text) {
|
|
773
925
|
return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").trim();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
hasQmd,
|
|
3
|
-
withQmdIndexArgs
|
|
4
|
-
} from "./chunk-EK6S23ZB.js";
|
|
5
1
|
import {
|
|
6
2
|
DEFAULT_CATEGORIES
|
|
7
3
|
} from "./chunk-2CDEETQN.js";
|
|
4
|
+
import {
|
|
5
|
+
hasQmd,
|
|
6
|
+
withQmdIndexArgs
|
|
7
|
+
} from "./chunk-PTWPPVC7.js";
|
|
8
8
|
|
|
9
9
|
// src/commands/setup.ts
|
|
10
10
|
import * as fs from "fs";
|
|
@@ -347,7 +347,7 @@ async function setupCommand(options = {}) {
|
|
|
347
347
|
console.log("\u2298 qmd collection already exists.");
|
|
348
348
|
}
|
|
349
349
|
} else {
|
|
350
|
-
console.log("\u2298 qmd not found \u2014 skipping
|
|
350
|
+
console.log("\u2298 qmd not found \u2014 skipping optional qmd fallback setup.");
|
|
351
351
|
}
|
|
352
352
|
console.log("\nTip: add this to your shell config:");
|
|
353
353
|
console.log(` export CLAWVAULT_PATH="${target.vaultPath}"`);
|
|
@@ -44,9 +44,31 @@ function resolveAgentVaultPath(agentVaults, agentId) {
|
|
|
44
44
|
if (!agentPath || typeof agentPath !== "string") return null;
|
|
45
45
|
return validateVaultPath(agentPath);
|
|
46
46
|
}
|
|
47
|
+
function toNonEmptyPathString(value) {
|
|
48
|
+
if (typeof value !== "string") return null;
|
|
49
|
+
const trimmed = value.trim();
|
|
50
|
+
return trimmed ? trimmed : null;
|
|
51
|
+
}
|
|
52
|
+
function coercePathCandidate(value) {
|
|
53
|
+
const direct = toNonEmptyPathString(value);
|
|
54
|
+
if (direct) return direct;
|
|
55
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const record = value;
|
|
59
|
+
const candidates = [record.explicitPath, record.vaultPath, record.path, record.vault];
|
|
60
|
+
for (const candidate of candidates) {
|
|
61
|
+
const normalized = toNonEmptyPathString(candidate);
|
|
62
|
+
if (normalized) {
|
|
63
|
+
return normalized;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
47
68
|
function resolveVaultPath(options = {}) {
|
|
48
|
-
|
|
49
|
-
|
|
69
|
+
const explicitPath = coercePathCandidate(options.explicitPath);
|
|
70
|
+
if (explicitPath) {
|
|
71
|
+
return path.resolve(explicitPath);
|
|
50
72
|
}
|
|
51
73
|
if (options.agentId && options.pluginConfig?.agentVaults) {
|
|
52
74
|
const agentVaultPath = resolveAgentVaultPath(
|
|
@@ -57,12 +79,14 @@ function resolveVaultPath(options = {}) {
|
|
|
57
79
|
return agentVaultPath;
|
|
58
80
|
}
|
|
59
81
|
}
|
|
60
|
-
|
|
61
|
-
|
|
82
|
+
const configuredVaultPath = coercePathCandidate(options.pluginConfig?.vaultPath);
|
|
83
|
+
if (configuredVaultPath) {
|
|
84
|
+
const validated = validateVaultPath(configuredVaultPath);
|
|
62
85
|
if (validated) return validated;
|
|
63
86
|
}
|
|
64
|
-
|
|
65
|
-
|
|
87
|
+
const envVaultPath = toNonEmptyPathString(process.env.CLAWVAULT_PATH);
|
|
88
|
+
if (envVaultPath) {
|
|
89
|
+
return path.resolve(envVaultPath);
|
|
66
90
|
}
|
|
67
91
|
const discovered = findNearestVaultPath(options.cwd ?? process.cwd());
|
|
68
92
|
if (discovered) {
|