clawvault 3.2.1 → 3.4.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 +56 -16
- package/bin/clawvault.js +0 -2
- package/bin/command-registration.test.js +15 -2
- package/bin/help-contract.test.js +16 -0
- package/bin/register-core-commands.js +88 -0
- package/bin/register-core-commands.test.js +80 -0
- package/bin/register-maintenance-commands.js +84 -7
- package/bin/register-query-commands.js +45 -28
- package/bin/register-query-commands.test.js +15 -0
- package/bin/test-helpers/cli-command-fixtures.js +1 -0
- package/dist/chunk-2PKBIKDH.js +130 -0
- package/dist/{chunk-U67V476Y.js → chunk-2ZDO52B4.js} +18 -1
- package/dist/{chunk-ZZA73MFY.js → chunk-33DOSHTA.js} +176 -36
- package/dist/chunk-35JCYSRR.js +158 -0
- package/dist/{chunk-AZYOKJYC.js → chunk-4PY655YM.js} +13 -1
- package/dist/{chunk-2JQ3O2YL.js → chunk-5EFSWZO6.js} +3 -3
- package/dist/{chunk-Y3TIJEBP.js → chunk-7SWP5FKU.js} +34 -613
- package/dist/{chunk-4VQTUVH7.js → chunk-7YZWHM36.js} +52 -26
- package/dist/{chunk-URXDAUVH.js → chunk-AXSJIFOJ.js} +174 -1
- package/dist/{chunk-4ITRXIVT.js → chunk-BLQXXX7Q.js} +6 -6
- package/dist/chunk-CSHO3PJB.js +684 -0
- package/dist/chunk-D5U3Q4N5.js +872 -0
- package/dist/chunk-DCF4KMFD.js +158 -0
- package/dist/{chunk-S5OJEGFG.js → chunk-DOIUYIXV.js} +2 -2
- package/dist/{chunk-YXQCA6B7.js → chunk-DVOUSOR3.js} +112 -7
- package/dist/{chunk-YDWHS4LJ.js → chunk-ECGJYWNA.js} +205 -33
- package/dist/{chunk-QMHPQYUV.js → chunk-EL6UBSX5.js} +7 -6
- package/dist/chunk-FZ5I2NF7.js +352 -0
- package/dist/{chunk-WJVWINEM.js → chunk-GFCHWMGD.js} +55 -6
- 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-UCQAOZHW.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-P5EPF6MB.js → chunk-MW5C6ZQA.js} +110 -13
- package/dist/chunk-NSXYM6EZ.js +255 -0
- package/dist/{chunk-YNIPYN4F.js → chunk-OFOCU2V4.js} +6 -5
- package/dist/{chunk-42MXU7A6.js → chunk-P62WHA27.js} +58 -47
- package/dist/chunk-PTWPPVC7.js +972 -0
- package/dist/{chunk-FAKNOB7Y.js → chunk-QFWERBDP.js} +2 -2
- package/dist/chunk-QYQAGBTM.js +2097 -0
- package/dist/chunk-RL2L6I6K.js +223 -0
- package/dist/{chunk-IIOU45CK.js → chunk-S7N7HI5E.js} +2 -2
- package/dist/{chunk-ECRZL5XR.js → chunk-T7E764W3.js} +23 -7
- package/dist/{chunk-MNPUYCHQ.js → chunk-TWMI3SNN.js} +6 -5
- package/dist/{chunk-2RAZ4ZFE.js → chunk-VBILES4B.js} +1 -1
- package/dist/{chunk-PI4WMLMG.js → chunk-VXAGOLDP.js} +1 -1
- package/dist/{chunk-SS4B7P7V.js → chunk-YIDV4VV2.js} +1 -1
- package/dist/chunk-YTRZNA64.js +37 -0
- package/dist/chunk-ZKWPCBYT.js +600 -0
- package/dist/cli/index.js +28 -21
- package/dist/commands/archive.js +3 -3
- package/dist/commands/backlog.js +1 -1
- package/dist/commands/benchmark.d.ts +12 -0
- package/dist/commands/benchmark.js +12 -0
- package/dist/commands/blocked.js +1 -1
- package/dist/commands/canvas.js +2 -2
- package/dist/commands/checkpoint.js +1 -1
- package/dist/commands/compat.js +1 -1
- package/dist/commands/context.js +8 -7
- package/dist/commands/doctor.d.ts +8 -3
- package/dist/commands/doctor.js +8 -22
- package/dist/commands/embed.js +6 -5
- package/dist/commands/entities.d.ts +8 -1
- package/dist/commands/entities.js +46 -3
- package/dist/commands/graph.js +4 -4
- 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 +5 -5
- package/dist/commands/kanban.js +1 -1
- package/dist/commands/link.js +5 -5
- package/dist/commands/maintain.d.ts +32 -0
- package/dist/commands/maintain.js +13 -0
- package/dist/commands/migrate-observations.js +3 -3
- package/dist/commands/observe.js +11 -10
- package/dist/commands/project.js +2 -2
- package/dist/commands/rebuild-embeddings.js +48 -17
- package/dist/commands/rebuild.js +9 -8
- package/dist/commands/recall.d.ts +14 -0
- package/dist/commands/recall.js +15 -0
- package/dist/commands/recover.js +1 -1
- package/dist/commands/reflect.js +6 -6
- package/dist/commands/repair-session.js +1 -1
- package/dist/commands/replay.js +10 -9
- package/dist/commands/session-recap.js +1 -1
- package/dist/commands/setup.js +4 -3
- package/dist/commands/shell-init.js +1 -1
- package/dist/commands/sleep.d.ts +1 -1
- package/dist/commands/sleep.js +20 -18
- package/dist/commands/status.js +40 -26
- package/dist/commands/sync-bd.js +3 -3
- package/dist/commands/tailscale.js +3 -3
- package/dist/commands/task.js +1 -1
- package/dist/commands/template.js +1 -1
- package/dist/commands/wake.d.ts +1 -1
- package/dist/commands/wake.js +10 -9
- package/dist/index.d.ts +233 -16
- package/dist/index.js +325 -111
- package/dist/{inject-DYUrDqQO.d.ts → inject-DEb_jpLi.d.ts} +3 -1
- package/dist/lib/auto-linker.js +2 -2
- package/dist/lib/canvas-layout.js +1 -1
- package/dist/lib/config.js +2 -2
- package/dist/lib/entity-index.js +1 -1
- package/dist/lib/project-utils.js +2 -2
- package/dist/lib/session-repair.js +1 -1
- package/dist/lib/session-utils.js +1 -1
- package/dist/lib/tailscale.js +1 -1
- package/dist/lib/task-utils.js +1 -1
- package/dist/lib/template-engine.js +1 -1
- package/dist/lib/webdav.js +1 -1
- package/dist/onnxruntime_binding-5QEF3SUC.node +0 -0
- package/dist/onnxruntime_binding-BKPKNEGC.node +0 -0
- package/dist/onnxruntime_binding-FMOXGIUT.node +0 -0
- package/dist/onnxruntime_binding-OI2KMXC5.node +0 -0
- package/dist/onnxruntime_binding-UX44MLAZ.node +0 -0
- package/dist/onnxruntime_binding-Y2W7N7WY.node +0 -0
- package/dist/openclaw-plugin--gqA2BZw.d.ts +267 -0
- package/dist/openclaw-plugin.d.ts +4 -0
- package/dist/openclaw-plugin.js +20 -0
- package/dist/transformers.node-A2ZRORSQ.js +46775 -0
- package/dist/types-CbL-wIKi.d.ts +36 -0
- package/dist/{types-BbWJoC1c.d.ts → types-DslKvCaj.d.ts} +51 -1
- package/hooks/clawvault/HOOK.md +25 -8
- package/hooks/clawvault/handler.js +215 -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 +161 -194
- package/package.json +8 -5
- package/bin/register-workgraph-commands.js +0 -451
- package/dist/chunk-5PJ4STIC.js +0 -465
- package/dist/chunk-ERNE2FZ5.js +0 -189
- package/dist/chunk-HR4KN6S2.js +0 -152
- package/dist/chunk-IJBFGPCS.js +0 -33
- package/dist/chunk-K7PNYS45.js +0 -93
- package/dist/chunk-NTOPJI7W.js +0 -207
- package/dist/chunk-PG56HX5T.js +0 -154
- package/dist/chunk-QPDDIHXE.js +0 -501
- package/dist/chunk-WIOLLGAD.js +0 -190
- package/dist/chunk-WMGIIABP.js +0 -15
- package/dist/ledger-B7g7jhqG.d.ts +0 -44
- package/dist/plugin/index.d.ts +0 -352
- package/dist/plugin/index.js +0 -4264
- 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
|
@@ -4,12 +4,100 @@ import * as path from "path";
|
|
|
4
4
|
var JSONL_SAMPLE_LIMIT = 20;
|
|
5
5
|
var MARKDOWN_SIGNAL_RE = /^(#{1,6}\s|[-*+]\s|>\s)/;
|
|
6
6
|
var MARKDOWN_INLINE_RE = /(\[[^\]]+\]\([^)]+\)|[*_`~])/;
|
|
7
|
+
var BASE64_DATA_URI_RE = /\bdata:[^;\s]+;base64,[A-Za-z0-9+/=]{24,}\b/gi;
|
|
8
|
+
var LONG_BASE64_TOKEN_RE = /\b[A-Za-z0-9+/]{80,}={0,2}\b/g;
|
|
9
|
+
var STRUCTURED_NOISE_MARKER_RE = /\b(?:tool[_-]?result|tool[_-]?use|toolcallid|tooluseid|function[_-]?(?:call|result)|stdout|stderr|exitcode|recordedat|trace(?:_|-)?id|parent(?:_|-)?id|session(?:_|-)?id|metadata|base64|mime(?:type)?)\b/i;
|
|
10
|
+
var NOISY_PREFIX_RE = /^(?:metadata|system metadata|session metadata)\s*:/i;
|
|
11
|
+
function normalizeToken(value) {
|
|
12
|
+
return value.trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
13
|
+
}
|
|
7
14
|
function normalizeText(value) {
|
|
8
15
|
return value.replace(/\s+/g, " ").trim();
|
|
9
16
|
}
|
|
17
|
+
function shouldDropRole(role) {
|
|
18
|
+
const normalized = normalizeToken(role);
|
|
19
|
+
if (!normalized) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (normalized === "system" || normalized === "developer" || normalized === "metadata") {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return normalized.startsWith("tool");
|
|
26
|
+
}
|
|
27
|
+
function isConversationRolePrefix(role) {
|
|
28
|
+
const normalized = normalizeToken(role);
|
|
29
|
+
if (!normalized) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (normalized === "user" || normalized === "assistant" || normalized === "system") {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (normalized === "developer" || normalized === "metadata") {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return normalized.startsWith("tool");
|
|
39
|
+
}
|
|
40
|
+
function isNoisyBlockType(value) {
|
|
41
|
+
if (typeof value !== "string") {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const normalized = normalizeToken(value);
|
|
45
|
+
if (!normalized || normalized === "text" || normalized === "markdown") {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return normalized.includes("tool") || normalized.includes("functioncall") || normalized.includes("functionresult") || normalized.includes("thinking") || normalized.includes("reason") || normalized.includes("metadata");
|
|
49
|
+
}
|
|
50
|
+
function stripNoisyData(value) {
|
|
51
|
+
return normalizeText(
|
|
52
|
+
value.replace(BASE64_DATA_URI_RE, " ").replace(LONG_BASE64_TOKEN_RE, " ")
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
function isLikelyStructuredNoise(value) {
|
|
56
|
+
const trimmed = value.trim();
|
|
57
|
+
if (!trimmed) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
if (NOISY_PREFIX_RE.test(trimmed)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
const looksStructured = trimmed.startsWith("{") || trimmed.startsWith("[");
|
|
64
|
+
if (looksStructured && STRUCTURED_NOISE_MARKER_RE.test(trimmed) && trimmed.length >= 40) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
function sanitizeExtractedText(value) {
|
|
70
|
+
const stripped = stripNoisyData(value);
|
|
71
|
+
if (!stripped) {
|
|
72
|
+
return "";
|
|
73
|
+
}
|
|
74
|
+
if (isLikelyStructuredNoise(stripped)) {
|
|
75
|
+
return "";
|
|
76
|
+
}
|
|
77
|
+
return stripped;
|
|
78
|
+
}
|
|
79
|
+
function sanitizeParsedMessage(message) {
|
|
80
|
+
const normalized = normalizeText(message);
|
|
81
|
+
if (!normalized) {
|
|
82
|
+
return "";
|
|
83
|
+
}
|
|
84
|
+
const roleMatch = /^([a-z][a-z0-9_-]{1,31})\s*:\s*(.+)$/i.exec(normalized);
|
|
85
|
+
if (roleMatch && isConversationRolePrefix(roleMatch[1])) {
|
|
86
|
+
const role = normalizeRole(roleMatch[1]);
|
|
87
|
+
if (shouldDropRole(role)) {
|
|
88
|
+
return "";
|
|
89
|
+
}
|
|
90
|
+
const content = sanitizeExtractedText(roleMatch[2]);
|
|
91
|
+
if (!content) {
|
|
92
|
+
return "";
|
|
93
|
+
}
|
|
94
|
+
return role ? `${role}: ${content}` : content;
|
|
95
|
+
}
|
|
96
|
+
return sanitizeExtractedText(normalized);
|
|
97
|
+
}
|
|
10
98
|
function extractText(value) {
|
|
11
99
|
if (typeof value === "string") {
|
|
12
|
-
return
|
|
100
|
+
return sanitizeExtractedText(value);
|
|
13
101
|
}
|
|
14
102
|
if (Array.isArray(value)) {
|
|
15
103
|
const parts = [];
|
|
@@ -25,11 +113,17 @@ function extractText(value) {
|
|
|
25
113
|
return "";
|
|
26
114
|
}
|
|
27
115
|
const record = value;
|
|
116
|
+
if (isNoisyBlockType(record.type)) {
|
|
117
|
+
return "";
|
|
118
|
+
}
|
|
28
119
|
if (typeof record.text === "string") {
|
|
29
|
-
return
|
|
120
|
+
return sanitizeExtractedText(record.text);
|
|
30
121
|
}
|
|
31
122
|
if (typeof record.content === "string") {
|
|
32
|
-
return
|
|
123
|
+
return sanitizeExtractedText(record.content);
|
|
124
|
+
}
|
|
125
|
+
if (record.content !== void 0) {
|
|
126
|
+
return extractText(record.content);
|
|
33
127
|
}
|
|
34
128
|
return "";
|
|
35
129
|
}
|
|
@@ -69,16 +163,18 @@ function parseJsonLine(line) {
|
|
|
69
163
|
const entry = parsed;
|
|
70
164
|
if ("role" in entry && "content" in entry) {
|
|
71
165
|
const role = normalizeRole(entry.role);
|
|
166
|
+
if (shouldDropRole(role)) return "";
|
|
72
167
|
const content = extractText(entry.content);
|
|
73
168
|
if (!content) return "";
|
|
74
|
-
return role ? `${role}: ${content}` : content;
|
|
169
|
+
return sanitizeParsedMessage(role ? `${role}: ${content}` : content);
|
|
75
170
|
}
|
|
76
171
|
if (entry.type === "message" && entry.message && typeof entry.message === "object") {
|
|
77
172
|
const message = entry.message;
|
|
78
173
|
const role = normalizeRole(message.role);
|
|
174
|
+
if (shouldDropRole(role)) return "";
|
|
79
175
|
const content = extractText(message.content);
|
|
80
176
|
if (!content) return "";
|
|
81
|
-
return role ? `${role}: ${content}` : content;
|
|
177
|
+
return sanitizeParsedMessage(role ? `${role}: ${content}` : content);
|
|
82
178
|
}
|
|
83
179
|
return "";
|
|
84
180
|
}
|
|
@@ -119,17 +215,21 @@ function parseMarkdown(raw) {
|
|
|
119
215
|
if (roleMatch) {
|
|
120
216
|
const role = normalizeRole(roleMatch[1]);
|
|
121
217
|
const content = normalizeText(roleMatch[2]);
|
|
122
|
-
|
|
123
|
-
|
|
218
|
+
const parsed2 = sanitizeParsedMessage(`${role}: ${content}`);
|
|
219
|
+
if (parsed2) {
|
|
220
|
+
messages.push(parsed2);
|
|
124
221
|
}
|
|
125
222
|
continue;
|
|
126
223
|
}
|
|
127
|
-
|
|
224
|
+
const parsed = sanitizeParsedMessage(joined);
|
|
225
|
+
if (parsed) {
|
|
226
|
+
messages.push(parsed);
|
|
227
|
+
}
|
|
128
228
|
}
|
|
129
229
|
return messages;
|
|
130
230
|
}
|
|
131
231
|
function parsePlainText(raw) {
|
|
132
|
-
return raw.split(/\r?\n/).map((line) =>
|
|
232
|
+
return raw.split(/\r?\n/).map((line) => sanitizeParsedMessage(line)).filter(Boolean);
|
|
133
233
|
}
|
|
134
234
|
function detectSessionFormat(raw, filePath) {
|
|
135
235
|
const nonEmptyLines = raw.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
@@ -163,10 +263,7 @@ function parseSessionFile(filePath) {
|
|
|
163
263
|
const raw = fs.readFileSync(resolved, "utf-8");
|
|
164
264
|
const format = detectSessionFormat(raw, resolved);
|
|
165
265
|
if (format === "jsonl") {
|
|
166
|
-
|
|
167
|
-
if (parsed.length > 0) {
|
|
168
|
-
return parsed;
|
|
169
|
-
}
|
|
266
|
+
return parseJsonLines(raw);
|
|
170
267
|
}
|
|
171
268
|
if (format === "markdown") {
|
|
172
269
|
const parsed = parseMarkdown(raw);
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ClawVault
|
|
3
|
+
} from "./chunk-ECGJYWNA.js";
|
|
4
|
+
|
|
5
|
+
// src/entities/synthesis.ts
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import * as path from "path";
|
|
8
|
+
import matter from "gray-matter";
|
|
9
|
+
function slugify(value) {
|
|
10
|
+
return value.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").trim();
|
|
11
|
+
}
|
|
12
|
+
function cleanEntityName(value) {
|
|
13
|
+
const stripped = value.replace(/[#*_`]/g, "").trim();
|
|
14
|
+
if (!stripped) return stripped;
|
|
15
|
+
if (/[A-Z]/.test(stripped)) {
|
|
16
|
+
return stripped;
|
|
17
|
+
}
|
|
18
|
+
return stripped.replace(/[-_]+/g, " ").split(/\s+/).filter(Boolean).map((token) => token.charAt(0).toUpperCase() + token.slice(1)).join(" ");
|
|
19
|
+
}
|
|
20
|
+
function uniqueStrings(values) {
|
|
21
|
+
const seen = /* @__PURE__ */ new Set();
|
|
22
|
+
const out = [];
|
|
23
|
+
for (const value of values) {
|
|
24
|
+
const normalized = value.trim();
|
|
25
|
+
if (!normalized) continue;
|
|
26
|
+
const key = normalized.toLowerCase();
|
|
27
|
+
if (seen.has(key)) continue;
|
|
28
|
+
seen.add(key);
|
|
29
|
+
out.push(normalized);
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
function extractBestSnippet(document) {
|
|
34
|
+
const content = document.content.replace(/\s+/g, " ").trim();
|
|
35
|
+
if (!content) return `Referenced in ${document.id}`;
|
|
36
|
+
return content.slice(0, 180);
|
|
37
|
+
}
|
|
38
|
+
function inferEntityKind(name, mentions) {
|
|
39
|
+
const categoryCounts = /* @__PURE__ */ new Map();
|
|
40
|
+
for (const mention of mentions) {
|
|
41
|
+
categoryCounts.set(
|
|
42
|
+
mention.document.category,
|
|
43
|
+
(categoryCounts.get(mention.document.category) ?? 0) + 1
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
if ((categoryCounts.get("people") ?? 0) >= (categoryCounts.get("projects") ?? 0) && (categoryCounts.get("people") ?? 0) > 0) {
|
|
47
|
+
return "person";
|
|
48
|
+
}
|
|
49
|
+
if ((categoryCounts.get("projects") ?? 0) > 0) {
|
|
50
|
+
return "project";
|
|
51
|
+
}
|
|
52
|
+
if (/\b(inc|corp|llc|ltd|org|foundation|company)\b/i.test(name)) {
|
|
53
|
+
return "org";
|
|
54
|
+
}
|
|
55
|
+
if (/\b(city|county|street|park|mount|river|lake|bay)\b/i.test(name)) {
|
|
56
|
+
return "place";
|
|
57
|
+
}
|
|
58
|
+
return "unknown";
|
|
59
|
+
}
|
|
60
|
+
function buildSummary(name, mentions) {
|
|
61
|
+
if (mentions.length === 0) {
|
|
62
|
+
return `${name} is tracked in ClawVault memories.`;
|
|
63
|
+
}
|
|
64
|
+
const newest = [...mentions].sort(
|
|
65
|
+
(left, right) => right.document.modified.getTime() - left.document.modified.getTime()
|
|
66
|
+
)[0];
|
|
67
|
+
const snippet = newest?.snippet ?? `${name} is referenced in memory files.`;
|
|
68
|
+
return snippet.endsWith(".") ? snippet : `${snippet}.`;
|
|
69
|
+
}
|
|
70
|
+
function toIsoDate(value) {
|
|
71
|
+
return value.toISOString();
|
|
72
|
+
}
|
|
73
|
+
function buildRelationshipGraph(entityMentions) {
|
|
74
|
+
const graph = /* @__PURE__ */ new Map();
|
|
75
|
+
for (const [entityName, mentions] of entityMentions.entries()) {
|
|
76
|
+
for (const mention of mentions) {
|
|
77
|
+
const coMentioned = uniqueStrings(
|
|
78
|
+
mention.document.links.map((link) => cleanEntityName(link))
|
|
79
|
+
).filter((candidate) => candidate.toLowerCase() !== entityName.toLowerCase());
|
|
80
|
+
if (coMentioned.length === 0) continue;
|
|
81
|
+
if (!graph.has(entityName)) {
|
|
82
|
+
graph.set(entityName, /* @__PURE__ */ new Map());
|
|
83
|
+
}
|
|
84
|
+
const adjacency = graph.get(entityName);
|
|
85
|
+
for (const related of coMentioned) {
|
|
86
|
+
const current = adjacency.get(related) ?? {
|
|
87
|
+
target: related,
|
|
88
|
+
count: 0,
|
|
89
|
+
evidence: /* @__PURE__ */ new Set()
|
|
90
|
+
};
|
|
91
|
+
current.count += 1;
|
|
92
|
+
current.evidence.add(mention.document.id);
|
|
93
|
+
adjacency.set(related, current);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return graph;
|
|
98
|
+
}
|
|
99
|
+
function toRelationships(accumulator) {
|
|
100
|
+
if (!accumulator) return [];
|
|
101
|
+
return [...accumulator.values()].sort((left, right) => right.count - left.count).slice(0, 8).map((entry) => ({
|
|
102
|
+
target: entry.target,
|
|
103
|
+
strength: entry.count,
|
|
104
|
+
evidence: [...entry.evidence].slice(0, 5)
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
function renderEntityProfileMarkdown(profile) {
|
|
108
|
+
const frontmatter = {
|
|
109
|
+
title: profile.name,
|
|
110
|
+
aliases: profile.aliases,
|
|
111
|
+
kind: profile.kind,
|
|
112
|
+
lastMentioned: profile.lastMentioned,
|
|
113
|
+
relationships: profile.relationships.map((relationship) => ({
|
|
114
|
+
target: relationship.target,
|
|
115
|
+
strength: relationship.strength,
|
|
116
|
+
evidence: relationship.evidence
|
|
117
|
+
}))
|
|
118
|
+
};
|
|
119
|
+
const relationshipLines = profile.relationships.length > 0 ? profile.relationships.map((relationship) => `- [[${relationship.target}]] (strength: ${relationship.strength})`).join("\n") : "- No relationships recorded yet.";
|
|
120
|
+
const body = [
|
|
121
|
+
`# ${profile.name}`,
|
|
122
|
+
"",
|
|
123
|
+
profile.summary,
|
|
124
|
+
"",
|
|
125
|
+
"## Relationships",
|
|
126
|
+
relationshipLines,
|
|
127
|
+
"",
|
|
128
|
+
"## Metadata",
|
|
129
|
+
`- Kind: ${profile.kind}`,
|
|
130
|
+
`- Last mentioned: ${profile.lastMentioned}`
|
|
131
|
+
].join("\n");
|
|
132
|
+
return matter.stringify(body, frontmatter);
|
|
133
|
+
}
|
|
134
|
+
function parseEntityProfile(filePath) {
|
|
135
|
+
const parsed = matter(fs.readFileSync(filePath, "utf-8"));
|
|
136
|
+
const title = typeof parsed.data.title === "string" ? parsed.data.title : path.basename(filePath, ".md");
|
|
137
|
+
const aliases = Array.isArray(parsed.data.aliases) ? parsed.data.aliases.map((value) => String(value)) : [title];
|
|
138
|
+
const kind = typeof parsed.data.kind === "string" ? parsed.data.kind : "unknown";
|
|
139
|
+
const relationships = Array.isArray(parsed.data.relationships) ? parsed.data.relationships.map((value) => {
|
|
140
|
+
if (!value || typeof value !== "object") return null;
|
|
141
|
+
const record = value;
|
|
142
|
+
if (typeof record.target !== "string") return null;
|
|
143
|
+
const strengthValue = typeof record.strength === "number" ? record.strength : Number.parseFloat(String(record.strength ?? "1"));
|
|
144
|
+
return {
|
|
145
|
+
target: record.target,
|
|
146
|
+
strength: Number.isFinite(strengthValue) ? strengthValue : 1,
|
|
147
|
+
evidence: Array.isArray(record.evidence) ? record.evidence.map((entry) => String(entry)) : []
|
|
148
|
+
};
|
|
149
|
+
}).filter((value) => Boolean(value)) : [];
|
|
150
|
+
const summary = parsed.content.split(/\r?\n/).map((line) => line.trim()).find((line) => line && !line.startsWith("#") && !line.startsWith("-")) ?? `${title} profile`;
|
|
151
|
+
const lastMentioned = typeof parsed.data.lastMentioned === "string" ? parsed.data.lastMentioned : (/* @__PURE__ */ new Date(0)).toISOString();
|
|
152
|
+
return {
|
|
153
|
+
name: title,
|
|
154
|
+
aliases: uniqueStrings([title, ...aliases]),
|
|
155
|
+
kind,
|
|
156
|
+
summary,
|
|
157
|
+
relationships,
|
|
158
|
+
lastMentioned
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
async function collectEntityMentions(vaultPath) {
|
|
162
|
+
const vault = new ClawVault(vaultPath);
|
|
163
|
+
await vault.load();
|
|
164
|
+
const docs = await vault.list();
|
|
165
|
+
const map = /* @__PURE__ */ new Map();
|
|
166
|
+
for (const document of docs) {
|
|
167
|
+
const names = uniqueStrings(
|
|
168
|
+
document.category === "people" || document.category === "projects" ? [document.title, ...document.links.map((link) => cleanEntityName(link))] : document.links.map((link) => cleanEntityName(link))
|
|
169
|
+
);
|
|
170
|
+
for (const name of names) {
|
|
171
|
+
if (!map.has(name)) {
|
|
172
|
+
map.set(name, []);
|
|
173
|
+
}
|
|
174
|
+
map.get(name).push({
|
|
175
|
+
document,
|
|
176
|
+
snippet: extractBestSnippet(document)
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return map;
|
|
181
|
+
}
|
|
182
|
+
async function synthesizeEntityProfiles(vaultPath, options = {}) {
|
|
183
|
+
const mentions = await collectEntityMentions(vaultPath);
|
|
184
|
+
const relationshipGraph = buildRelationshipGraph(mentions);
|
|
185
|
+
const profiles = [];
|
|
186
|
+
for (const [name, records] of mentions.entries()) {
|
|
187
|
+
const kind = inferEntityKind(name, records);
|
|
188
|
+
const relationships = toRelationships(relationshipGraph.get(name));
|
|
189
|
+
const aliases = uniqueStrings([
|
|
190
|
+
name,
|
|
191
|
+
slugify(name).replace(/-/g, " ")
|
|
192
|
+
]);
|
|
193
|
+
const lastMentioned = toIsoDate(
|
|
194
|
+
records.reduce(
|
|
195
|
+
(latest, record) => record.document.modified.getTime() > latest.getTime() ? record.document.modified : latest,
|
|
196
|
+
/* @__PURE__ */ new Date(0)
|
|
197
|
+
)
|
|
198
|
+
);
|
|
199
|
+
profiles.push({
|
|
200
|
+
name,
|
|
201
|
+
aliases,
|
|
202
|
+
kind,
|
|
203
|
+
summary: buildSummary(name, records),
|
|
204
|
+
relationships,
|
|
205
|
+
lastMentioned
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
profiles.sort((left, right) => right.lastMentioned.localeCompare(left.lastMentioned));
|
|
209
|
+
if (options.writeFiles ?? true) {
|
|
210
|
+
const entitiesDir = path.join(vaultPath, "entities");
|
|
211
|
+
if (!fs.existsSync(entitiesDir)) {
|
|
212
|
+
fs.mkdirSync(entitiesDir, { recursive: true });
|
|
213
|
+
}
|
|
214
|
+
for (const profile of profiles) {
|
|
215
|
+
const targetPath = path.join(entitiesDir, `${slugify(profile.name)}.md`);
|
|
216
|
+
fs.writeFileSync(targetPath, renderEntityProfileMarkdown(profile), "utf-8");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return profiles;
|
|
220
|
+
}
|
|
221
|
+
function readEntityProfiles(vaultPath) {
|
|
222
|
+
const entitiesDir = path.join(vaultPath, "entities");
|
|
223
|
+
if (!fs.existsSync(entitiesDir)) {
|
|
224
|
+
return [];
|
|
225
|
+
}
|
|
226
|
+
const files = fs.readdirSync(entitiesDir).filter((entry) => entry.endsWith(".md"));
|
|
227
|
+
return files.map((entry) => parseEntityProfile(path.join(entitiesDir, entry))).sort((left, right) => right.lastMentioned.localeCompare(left.lastMentioned));
|
|
228
|
+
}
|
|
229
|
+
async function ensureEntityProfiles(vaultPath) {
|
|
230
|
+
const existing = readEntityProfiles(vaultPath);
|
|
231
|
+
if (existing.length > 0) {
|
|
232
|
+
return existing;
|
|
233
|
+
}
|
|
234
|
+
return synthesizeEntityProfiles(vaultPath, { writeFiles: true });
|
|
235
|
+
}
|
|
236
|
+
async function readEntityProfile(vaultPath, name) {
|
|
237
|
+
const profiles = await ensureEntityProfiles(vaultPath);
|
|
238
|
+
const normalized = name.trim().toLowerCase();
|
|
239
|
+
for (const profile of profiles) {
|
|
240
|
+
if (profile.name.toLowerCase() === normalized) {
|
|
241
|
+
return profile;
|
|
242
|
+
}
|
|
243
|
+
if (profile.aliases.some((alias) => alias.toLowerCase() === normalized)) {
|
|
244
|
+
return profile;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export {
|
|
251
|
+
synthesizeEntityProfiles,
|
|
252
|
+
readEntityProfiles,
|
|
253
|
+
ensureEntityProfiles,
|
|
254
|
+
readEntityProfile
|
|
255
|
+
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
listConfig
|
|
3
|
-
} from "./chunk-URXDAUVH.js";
|
|
4
1
|
import {
|
|
5
2
|
requestLlmCompletion,
|
|
6
3
|
resolveLlmProvider
|
|
7
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-DVOUSOR3.js";
|
|
5
|
+
import {
|
|
6
|
+
listConfig
|
|
7
|
+
} from "./chunk-AXSJIFOJ.js";
|
|
8
8
|
import {
|
|
9
9
|
getMemoryGraph,
|
|
10
10
|
loadMemoryGraphIndex
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-33DOSHTA.js";
|
|
12
12
|
|
|
13
13
|
// src/commands/inject.ts
|
|
14
14
|
import * as path2 from "path";
|
|
@@ -470,6 +470,7 @@ async function addLlmIntentMatches(params) {
|
|
|
470
470
|
provider: params.provider,
|
|
471
471
|
prompt,
|
|
472
472
|
model: params.model,
|
|
473
|
+
tier: "complex",
|
|
473
474
|
temperature: 0.1,
|
|
474
475
|
maxTokens: 1200,
|
|
475
476
|
fetchImpl: params.fetchImpl,
|
|
@@ -14,7 +14,7 @@ function findProtectedRanges(content) {
|
|
|
14
14
|
while ((match = codeBlockRegex.exec(content)) !== null) {
|
|
15
15
|
ranges.push({ start: match.index, end: match.index + match[0].length });
|
|
16
16
|
}
|
|
17
|
-
const inlineCodeRegex =
|
|
17
|
+
const inlineCodeRegex = /(?<!`)`[^`\n]+`(?!`)/g;
|
|
18
18
|
while ((match = inlineCodeRegex.exec(content)) !== null) {
|
|
19
19
|
ranges.push({ start: match.index, end: match.index + match[0].length });
|
|
20
20
|
}
|
|
@@ -26,10 +26,51 @@ function findProtectedRanges(content) {
|
|
|
26
26
|
while ((match = urlRegex.exec(content)) !== null) {
|
|
27
27
|
ranges.push({ start: match.index, end: match.index + match[0].length });
|
|
28
28
|
}
|
|
29
|
+
const filePathRegex = /(?:^|[\s([{"'])((?:~|\/)[^\s`<>\])}"']+|[A-Za-z]:\\[^\s`<>\])}"']+)/g;
|
|
30
|
+
while ((match = filePathRegex.exec(content)) !== null) {
|
|
31
|
+
const fullMatch = match[0];
|
|
32
|
+
const pathValue = match[1];
|
|
33
|
+
const start = match.index + fullMatch.indexOf(pathValue);
|
|
34
|
+
ranges.push({ start, end: start + pathValue.length });
|
|
35
|
+
}
|
|
29
36
|
return ranges;
|
|
30
37
|
}
|
|
31
|
-
function
|
|
32
|
-
return ranges.some((
|
|
38
|
+
function isProtectedRange(start, end, ranges) {
|
|
39
|
+
return ranges.some((range) => start < range.end && end > range.start);
|
|
40
|
+
}
|
|
41
|
+
function createAliasRegex(alias) {
|
|
42
|
+
const escapedAlias = alias.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
43
|
+
return new RegExp(`\\b${escapedAlias}\\b`, "gi");
|
|
44
|
+
}
|
|
45
|
+
function formatWikiLink(path, originalText) {
|
|
46
|
+
return originalText.toLowerCase() === path.split("/").pop()?.toLowerCase() ? `[[${path}]]` : `[[${path}|${originalText}]]`;
|
|
47
|
+
}
|
|
48
|
+
function planLinks(content, index, protectedRanges) {
|
|
49
|
+
const sortedAliases = getSortedAliases(index);
|
|
50
|
+
const linkedEntities = /* @__PURE__ */ new Set();
|
|
51
|
+
const claimedRanges = [];
|
|
52
|
+
const plannedLinks = [];
|
|
53
|
+
for (const { alias, path } of sortedAliases) {
|
|
54
|
+
if (linkedEntities.has(path)) continue;
|
|
55
|
+
const regex = createAliasRegex(alias);
|
|
56
|
+
let match;
|
|
57
|
+
while ((match = regex.exec(content)) !== null) {
|
|
58
|
+
const start = match.index;
|
|
59
|
+
const end = start + match[0].length;
|
|
60
|
+
if (isProtectedRange(start, end, protectedRanges)) continue;
|
|
61
|
+
if (isProtectedRange(start, end, claimedRanges)) continue;
|
|
62
|
+
plannedLinks.push({
|
|
63
|
+
start,
|
|
64
|
+
end,
|
|
65
|
+
originalText: match[0],
|
|
66
|
+
path
|
|
67
|
+
});
|
|
68
|
+
claimedRanges.push({ start, end });
|
|
69
|
+
linkedEntities.add(path);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return plannedLinks;
|
|
33
74
|
}
|
|
34
75
|
function createLineLookup(content) {
|
|
35
76
|
const lines = content.split("\n");
|
|
@@ -48,56 +89,24 @@ function createLineLookup(content) {
|
|
|
48
89
|
}
|
|
49
90
|
function autoLink(content, index) {
|
|
50
91
|
const protectedRanges = findProtectedRanges(content);
|
|
51
|
-
const
|
|
52
|
-
const linkedEntities = /* @__PURE__ */ new Set();
|
|
92
|
+
const plannedLinks = planLinks(content, index, protectedRanges);
|
|
53
93
|
let result = content;
|
|
54
|
-
|
|
55
|
-
for (const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const regex = new RegExp(`\\b${escapedAlias}\\b`, "gi");
|
|
59
|
-
let match;
|
|
60
|
-
while ((match = regex.exec(content)) !== null) {
|
|
61
|
-
const originalPos = match.index;
|
|
62
|
-
const adjustedPos = originalPos + offset;
|
|
63
|
-
if (isProtected(originalPos, protectedRanges)) continue;
|
|
64
|
-
const beforeMatch = result.substring(0, adjustedPos);
|
|
65
|
-
const openBrackets = (beforeMatch.match(/\[\[/g) || []).length;
|
|
66
|
-
const closeBrackets = (beforeMatch.match(/\]\]/g) || []).length;
|
|
67
|
-
if (openBrackets > closeBrackets) continue;
|
|
68
|
-
const originalText = match[0];
|
|
69
|
-
const replacement = originalText.toLowerCase() === path.split("/").pop()?.toLowerCase() ? `[[${path}]]` : `[[${path}|${originalText}]]`;
|
|
70
|
-
result = result.substring(0, adjustedPos) + replacement + result.substring(adjustedPos + originalText.length);
|
|
71
|
-
offset += replacement.length - originalText.length;
|
|
72
|
-
linkedEntities.add(path);
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
94
|
+
const sortedByPosition = plannedLinks.slice().sort((a, b) => b.start - a.start);
|
|
95
|
+
for (const planned of sortedByPosition) {
|
|
96
|
+
const replacement = formatWikiLink(planned.path, planned.originalText);
|
|
97
|
+
result = result.substring(0, planned.start) + replacement + result.substring(planned.end);
|
|
75
98
|
}
|
|
76
99
|
return result;
|
|
77
100
|
}
|
|
78
101
|
function dryRunLink(content, index) {
|
|
79
102
|
const protectedRanges = findProtectedRanges(content);
|
|
80
|
-
const
|
|
81
|
-
const linkedEntities = /* @__PURE__ */ new Set();
|
|
82
|
-
const matches = [];
|
|
103
|
+
const plannedLinks = planLinks(content, index, protectedRanges);
|
|
83
104
|
const getLineNumber = createLineLookup(content);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
while ((match = regex.exec(content)) !== null) {
|
|
90
|
-
if (isProtected(match.index, protectedRanges)) continue;
|
|
91
|
-
matches.push({
|
|
92
|
-
alias: match[0],
|
|
93
|
-
path,
|
|
94
|
-
line: getLineNumber(match.index)
|
|
95
|
-
});
|
|
96
|
-
linkedEntities.add(path);
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return matches;
|
|
105
|
+
return plannedLinks.map((planned) => ({
|
|
106
|
+
alias: planned.originalText,
|
|
107
|
+
path: planned.path,
|
|
108
|
+
line: getLineNumber(planned.start)
|
|
109
|
+
}));
|
|
101
110
|
}
|
|
102
111
|
function findUnlinkedMentions(content, index) {
|
|
103
112
|
const protectedRanges = findProtectedRanges(content);
|
|
@@ -111,7 +120,9 @@ function findUnlinkedMentions(content, index) {
|
|
|
111
120
|
const regex = new RegExp(`\\b${escapedAlias}\\b`, "gi");
|
|
112
121
|
let match;
|
|
113
122
|
while ((match = regex.exec(content)) !== null) {
|
|
114
|
-
|
|
123
|
+
const start = match.index;
|
|
124
|
+
const end = start + match[0].length;
|
|
125
|
+
if (isProtectedRange(start, end, protectedRanges)) continue;
|
|
115
126
|
matches.push({
|
|
116
127
|
alias: match[0],
|
|
117
128
|
path,
|