clawvault 2.6.3 → 2.6.5
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 +351 -21
- package/bin/clawvault.js +8 -2
- package/bin/command-runtime.js +9 -1
- package/bin/register-maintenance-commands.js +19 -0
- package/bin/register-query-commands.js +58 -6
- package/bin/register-workgraph-commands.js +451 -0
- package/dist/chunk-2GKPENIR.js +66 -0
- package/dist/{chunk-VXEOHTSL.js → chunk-2JQ3O2YL.js} +1 -1
- package/dist/{chunk-VR5NE7PZ.js → chunk-2RAZ4ZFE.js} +1 -1
- package/dist/chunk-2ZDO52B4.js +52 -0
- package/dist/{chunk-ZZA73MFY.js → chunk-33DOSHTA.js} +176 -36
- package/dist/chunk-4BQTQMJP.js +93 -0
- package/dist/{chunk-MAKNAHAW.js → chunk-5PJ4STIC.js} +98 -8
- package/dist/{chunk-RVYA52PY.js → chunk-5UM4PMMM.js} +1 -1
- package/dist/{chunk-4VQTUVH7.js → chunk-7YZWHM36.js} +52 -26
- package/dist/{chunk-4VRIMU4O.js → chunk-A4EAUO7T.js} +5 -5
- package/dist/{chunk-R6SXNSFD.js → chunk-BV5KWZKR.js} +3 -3
- package/dist/chunk-FBITHIZF.js +351 -0
- package/dist/{chunk-Q2J5YTUF.js → chunk-FUSLEY6L.js} +751 -34
- package/dist/chunk-GNJL4YGR.js +79 -0
- package/dist/{chunk-42MXU7A6.js → chunk-K4GFGKFD.js} +51 -47
- package/dist/{chunk-PBEE567J.js → chunk-KSZROBFH.js} +2 -2
- package/dist/chunk-L4HSSQ6T.js +152 -0
- package/dist/{chunk-PZ2AUU2W.js → chunk-LMKQ7NIF.js} +206 -37
- package/dist/{chunk-6546Q4OR.js → chunk-M5O6FQ66.js} +6 -6
- package/dist/chunk-MM6QGW3P.js +207 -0
- package/dist/{chunk-T76H47ZS.js → chunk-MNPUYCHQ.js} +1 -1
- package/dist/{chunk-P5EPF6MB.js → chunk-MW5C6ZQA.js} +110 -13
- package/dist/{chunk-OZ7RIXTO.js → chunk-QSRRMEYM.js} +2 -2
- package/dist/chunk-RHISK3SZ.js +189 -0
- package/dist/{chunk-3BTHWPMB.js → chunk-S5OJEGFG.js} +2 -2
- package/dist/{chunk-MGDEINGP.js → chunk-SS4B7P7V.js} +1 -1
- package/dist/{chunk-ME37YNW3.js → chunk-SV7T4HRE.js} +4 -4
- package/dist/{chunk-IEVLHNLU.js → chunk-T3FKSZSN.js} +3 -3
- package/dist/{chunk-DTEHFAL7.js → chunk-TS6NDVOU.js} +2 -2
- package/dist/chunk-U4O6C46S.js +154 -0
- package/dist/{chunk-ITPEXLHA.js → chunk-URXDAUVH.js} +24 -5
- package/dist/chunk-WMGIIABP.js +15 -0
- package/dist/{chunk-QVMXF7FY.js → chunk-X3SPPUFG.js} +50 -0
- package/dist/{chunk-THRJVD4L.js → chunk-Y6VJKXGL.js} +1 -1
- package/dist/{chunk-RCBMXTWS.js → chunk-YD7SVXTF.js} +39 -7
- package/dist/{chunk-HIHOUSXS.js → chunk-YXQCA6B7.js} +105 -1
- package/dist/cli/index.js +20 -18
- package/dist/commands/archive.js +3 -2
- package/dist/commands/backlog.js +1 -0
- package/dist/commands/blocked.js +1 -0
- package/dist/commands/canvas.js +2 -1
- package/dist/commands/checkpoint.js +1 -0
- package/dist/commands/compat.js +2 -1
- package/dist/commands/context.js +6 -4
- package/dist/commands/doctor.d.ts +10 -1
- package/dist/commands/doctor.js +13 -10
- package/dist/commands/embed.js +5 -3
- package/dist/commands/entities.js +2 -1
- package/dist/commands/graph.js +4 -3
- package/dist/commands/inject.d.ts +1 -1
- package/dist/commands/inject.js +5 -4
- package/dist/commands/kanban.js +1 -0
- package/dist/commands/link.js +5 -4
- package/dist/commands/migrate-observations.js +3 -2
- package/dist/commands/observe.js +9 -7
- package/dist/commands/project.js +1 -0
- package/dist/commands/rebuild-embeddings.d.ts +21 -0
- package/dist/commands/rebuild-embeddings.js +91 -0
- package/dist/commands/rebuild.js +6 -4
- package/dist/commands/recover.js +1 -0
- package/dist/commands/reflect.js +5 -4
- package/dist/commands/repair-session.js +1 -0
- package/dist/commands/replay.js +7 -6
- package/dist/commands/session-recap.js +1 -0
- package/dist/commands/setup.js +3 -2
- package/dist/commands/shell-init.js +2 -0
- package/dist/commands/sleep.d.ts +1 -1
- package/dist/commands/sleep.js +10 -8
- package/dist/commands/status.js +13 -82
- package/dist/commands/sync-bd.js +3 -2
- package/dist/commands/tailscale.js +3 -2
- package/dist/commands/task.js +1 -0
- package/dist/commands/template.js +1 -0
- package/dist/commands/wake.d.ts +1 -1
- package/dist/commands/wake.js +5 -3
- package/dist/index.d.ts +254 -10
- package/dist/index.js +288 -155
- package/dist/{inject-x65KXWPk.d.ts → inject-DYUrDqQO.d.ts} +2 -2
- package/dist/ledger-B7g7jhqG.d.ts +44 -0
- package/dist/lib/auto-linker.js +2 -1
- package/dist/lib/canvas-layout.js +1 -0
- package/dist/lib/config.d.ts +27 -3
- package/dist/lib/config.js +4 -1
- package/dist/lib/entity-index.js +1 -0
- package/dist/lib/project-utils.js +1 -0
- package/dist/lib/session-repair.js +1 -0
- package/dist/lib/session-utils.js +1 -0
- package/dist/lib/tailscale.js +1 -0
- package/dist/lib/task-utils.js +1 -0
- package/dist/lib/template-engine.js +1 -0
- package/dist/lib/webdav.js +1 -0
- 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/registry-BR4326o0.d.ts +30 -0
- package/dist/store-CA-6sKCJ.d.ts +34 -0
- package/dist/thread-B9LhXNU0.d.ts +41 -0
- package/dist/transformers.node-A2ZRORSQ.js +46775 -0
- package/dist/{types-C74wgGL1.d.ts → types-BbWJoC1c.d.ts} +1 -1
- package/dist/workgraph/index.d.ts +5 -0
- package/dist/workgraph/index.js +23 -0
- package/dist/workgraph/ledger.d.ts +2 -0
- package/dist/workgraph/ledger.js +25 -0
- package/dist/workgraph/registry.d.ts +2 -0
- package/dist/workgraph/registry.js +19 -0
- package/dist/workgraph/store.d.ts +2 -0
- package/dist/workgraph/store.js +25 -0
- package/dist/workgraph/thread.d.ts +2 -0
- package/dist/workgraph/thread.js +25 -0
- package/dist/workgraph/types.d.ts +54 -0
- package/dist/workgraph/types.js +7 -0
- package/hooks/clawvault/handler.js +714 -2
- package/hooks/clawvault/handler.test.js +153 -0
- package/hooks/clawvault/openclaw.plugin.json +72 -0
- package/openclaw.plugin.json +14 -2
- package/package.json +5 -4
- package/dist/chunk-4QYGFWRM.js +0 -88
- package/dist/chunk-MXSSG3QU.js +0 -42
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/lib/config.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
function getVaultPath() {
|
|
5
|
+
const vaultPath = process.env.CLAWVAULT_PATH;
|
|
6
|
+
if (!vaultPath) {
|
|
7
|
+
throw new Error("CLAWVAULT_PATH environment variable not set");
|
|
8
|
+
}
|
|
9
|
+
return path.resolve(vaultPath);
|
|
10
|
+
}
|
|
11
|
+
function findNearestVaultPath(startPath = process.cwd()) {
|
|
12
|
+
let current = path.resolve(startPath);
|
|
13
|
+
while (true) {
|
|
14
|
+
if (fs.existsSync(path.join(current, ".clawvault.json"))) {
|
|
15
|
+
return current;
|
|
16
|
+
}
|
|
17
|
+
const parent = path.dirname(current);
|
|
18
|
+
if (parent === current) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
current = parent;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function validateVaultPath(vaultPath) {
|
|
25
|
+
if (!vaultPath || typeof vaultPath !== "string") return null;
|
|
26
|
+
const resolved = path.resolve(vaultPath);
|
|
27
|
+
if (!path.isAbsolute(resolved)) return null;
|
|
28
|
+
try {
|
|
29
|
+
const stat = fs.statSync(resolved);
|
|
30
|
+
if (!stat.isDirectory()) return null;
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const configPath = path.join(resolved, ".clawvault.json");
|
|
35
|
+
if (!fs.existsSync(configPath)) return null;
|
|
36
|
+
return resolved;
|
|
37
|
+
}
|
|
38
|
+
function resolveAgentVaultPath(agentVaults, agentId) {
|
|
39
|
+
if (!agentId || typeof agentId !== "string") return null;
|
|
40
|
+
if (!agentVaults || typeof agentVaults !== "object" || Array.isArray(agentVaults)) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const agentPath = agentVaults[agentId];
|
|
44
|
+
if (!agentPath || typeof agentPath !== "string") return null;
|
|
45
|
+
return validateVaultPath(agentPath);
|
|
46
|
+
}
|
|
47
|
+
function resolveVaultPath(options = {}) {
|
|
48
|
+
if (options.explicitPath) {
|
|
49
|
+
return path.resolve(options.explicitPath);
|
|
50
|
+
}
|
|
51
|
+
if (options.agentId && options.pluginConfig?.agentVaults) {
|
|
52
|
+
const agentVaultPath = resolveAgentVaultPath(
|
|
53
|
+
options.pluginConfig.agentVaults,
|
|
54
|
+
options.agentId
|
|
55
|
+
);
|
|
56
|
+
if (agentVaultPath) {
|
|
57
|
+
return agentVaultPath;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (options.pluginConfig?.vaultPath) {
|
|
61
|
+
const validated = validateVaultPath(options.pluginConfig.vaultPath);
|
|
62
|
+
if (validated) return validated;
|
|
63
|
+
}
|
|
64
|
+
if (process.env.CLAWVAULT_PATH) {
|
|
65
|
+
return path.resolve(process.env.CLAWVAULT_PATH);
|
|
66
|
+
}
|
|
67
|
+
const discovered = findNearestVaultPath(options.cwd ?? process.cwd());
|
|
68
|
+
if (discovered) {
|
|
69
|
+
return discovered;
|
|
70
|
+
}
|
|
71
|
+
throw new Error("No vault path found. Set CLAWVAULT_PATH, use --vault, or run inside a vault.");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export {
|
|
75
|
+
getVaultPath,
|
|
76
|
+
findNearestVaultPath,
|
|
77
|
+
resolveAgentVaultPath,
|
|
78
|
+
resolveVaultPath
|
|
79
|
+
};
|
|
@@ -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
|
}
|
|
@@ -28,8 +28,42 @@ function findProtectedRanges(content) {
|
|
|
28
28
|
}
|
|
29
29
|
return ranges;
|
|
30
30
|
}
|
|
31
|
-
function
|
|
32
|
-
return ranges.some((
|
|
31
|
+
function isProtectedRange(start, end, ranges) {
|
|
32
|
+
return ranges.some((range) => start < range.end && end > range.start);
|
|
33
|
+
}
|
|
34
|
+
function createAliasRegex(alias) {
|
|
35
|
+
const escapedAlias = alias.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
36
|
+
return new RegExp(`\\b${escapedAlias}\\b`, "gi");
|
|
37
|
+
}
|
|
38
|
+
function formatWikiLink(path, originalText) {
|
|
39
|
+
return originalText.toLowerCase() === path.split("/").pop()?.toLowerCase() ? `[[${path}]]` : `[[${path}|${originalText}]]`;
|
|
40
|
+
}
|
|
41
|
+
function planLinks(content, index, protectedRanges) {
|
|
42
|
+
const sortedAliases = getSortedAliases(index);
|
|
43
|
+
const linkedEntities = /* @__PURE__ */ new Set();
|
|
44
|
+
const claimedRanges = [];
|
|
45
|
+
const plannedLinks = [];
|
|
46
|
+
for (const { alias, path } of sortedAliases) {
|
|
47
|
+
if (linkedEntities.has(path)) continue;
|
|
48
|
+
const regex = createAliasRegex(alias);
|
|
49
|
+
let match;
|
|
50
|
+
while ((match = regex.exec(content)) !== null) {
|
|
51
|
+
const start = match.index;
|
|
52
|
+
const end = start + match[0].length;
|
|
53
|
+
if (isProtectedRange(start, end, protectedRanges)) continue;
|
|
54
|
+
if (isProtectedRange(start, end, claimedRanges)) continue;
|
|
55
|
+
plannedLinks.push({
|
|
56
|
+
start,
|
|
57
|
+
end,
|
|
58
|
+
originalText: match[0],
|
|
59
|
+
path
|
|
60
|
+
});
|
|
61
|
+
claimedRanges.push({ start, end });
|
|
62
|
+
linkedEntities.add(path);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return plannedLinks;
|
|
33
67
|
}
|
|
34
68
|
function createLineLookup(content) {
|
|
35
69
|
const lines = content.split("\n");
|
|
@@ -48,56 +82,24 @@ function createLineLookup(content) {
|
|
|
48
82
|
}
|
|
49
83
|
function autoLink(content, index) {
|
|
50
84
|
const protectedRanges = findProtectedRanges(content);
|
|
51
|
-
const
|
|
52
|
-
const linkedEntities = /* @__PURE__ */ new Set();
|
|
85
|
+
const plannedLinks = planLinks(content, index, protectedRanges);
|
|
53
86
|
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
|
-
}
|
|
87
|
+
const sortedByPosition = plannedLinks.slice().sort((a, b) => b.start - a.start);
|
|
88
|
+
for (const planned of sortedByPosition) {
|
|
89
|
+
const replacement = formatWikiLink(planned.path, planned.originalText);
|
|
90
|
+
result = result.substring(0, planned.start) + replacement + result.substring(planned.end);
|
|
75
91
|
}
|
|
76
92
|
return result;
|
|
77
93
|
}
|
|
78
94
|
function dryRunLink(content, index) {
|
|
79
95
|
const protectedRanges = findProtectedRanges(content);
|
|
80
|
-
const
|
|
81
|
-
const linkedEntities = /* @__PURE__ */ new Set();
|
|
82
|
-
const matches = [];
|
|
96
|
+
const plannedLinks = planLinks(content, index, protectedRanges);
|
|
83
97
|
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;
|
|
98
|
+
return plannedLinks.map((planned) => ({
|
|
99
|
+
alias: planned.originalText,
|
|
100
|
+
path: planned.path,
|
|
101
|
+
line: getLineNumber(planned.start)
|
|
102
|
+
}));
|
|
101
103
|
}
|
|
102
104
|
function findUnlinkedMentions(content, index) {
|
|
103
105
|
const protectedRanges = findProtectedRanges(content);
|
|
@@ -111,7 +113,9 @@ function findUnlinkedMentions(content, index) {
|
|
|
111
113
|
const regex = new RegExp(`\\b${escapedAlias}\\b`, "gi");
|
|
112
114
|
let match;
|
|
113
115
|
while ((match = regex.exec(content)) !== null) {
|
|
114
|
-
|
|
116
|
+
const start = match.index;
|
|
117
|
+
const end = start + match[0].length;
|
|
118
|
+
if (isProtectedRange(start, end, protectedRanges)) continue;
|
|
115
119
|
matches.push({
|
|
116
120
|
alias: match[0],
|
|
117
121
|
path,
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getType,
|
|
3
|
+
loadRegistry
|
|
4
|
+
} from "./chunk-MM6QGW3P.js";
|
|
5
|
+
import {
|
|
6
|
+
append
|
|
7
|
+
} from "./chunk-4BQTQMJP.js";
|
|
8
|
+
import {
|
|
9
|
+
__export
|
|
10
|
+
} from "./chunk-2ZDO52B4.js";
|
|
11
|
+
|
|
12
|
+
// src/workgraph/store.ts
|
|
13
|
+
var store_exports = {};
|
|
14
|
+
__export(store_exports, {
|
|
15
|
+
activeThreads: () => activeThreads,
|
|
16
|
+
blockedThreads: () => blockedThreads,
|
|
17
|
+
create: () => create,
|
|
18
|
+
findByField: () => findByField,
|
|
19
|
+
list: () => list,
|
|
20
|
+
openThreads: () => openThreads,
|
|
21
|
+
read: () => read,
|
|
22
|
+
remove: () => remove,
|
|
23
|
+
update: () => update
|
|
24
|
+
});
|
|
25
|
+
import fs from "fs";
|
|
26
|
+
import path from "path";
|
|
27
|
+
import matter from "gray-matter";
|
|
28
|
+
function create(vaultPath, typeName, fields, body, actor) {
|
|
29
|
+
const typeDef = getType(vaultPath, typeName);
|
|
30
|
+
if (!typeDef) {
|
|
31
|
+
throw new Error(`Unknown primitive type "${typeName}". Run \`clawvault primitive list\` to see available types, or \`clawvault primitive define\` to create one.`);
|
|
32
|
+
}
|
|
33
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
34
|
+
const merged = applyDefaults(typeDef, {
|
|
35
|
+
...fields,
|
|
36
|
+
created: fields.created ?? now,
|
|
37
|
+
updated: now
|
|
38
|
+
});
|
|
39
|
+
const slug = slugify(String(merged.title ?? merged.name ?? typeName));
|
|
40
|
+
const relDir = typeDef.directory;
|
|
41
|
+
const relPath = `${relDir}/${slug}.md`;
|
|
42
|
+
const absDir = path.join(vaultPath, relDir);
|
|
43
|
+
const absPath = path.join(vaultPath, relPath);
|
|
44
|
+
if (!fs.existsSync(absDir)) fs.mkdirSync(absDir, { recursive: true });
|
|
45
|
+
if (fs.existsSync(absPath)) {
|
|
46
|
+
throw new Error(`File already exists: ${relPath}. Use update instead.`);
|
|
47
|
+
}
|
|
48
|
+
const content = matter.stringify(body, stripUndefined(merged));
|
|
49
|
+
fs.writeFileSync(absPath, content, "utf-8");
|
|
50
|
+
append(vaultPath, actor, "create", relPath, typeName, {
|
|
51
|
+
title: merged.title ?? slug
|
|
52
|
+
});
|
|
53
|
+
return { path: relPath, type: typeName, fields: merged, body };
|
|
54
|
+
}
|
|
55
|
+
function read(vaultPath, relPath) {
|
|
56
|
+
const absPath = path.join(vaultPath, relPath);
|
|
57
|
+
if (!fs.existsSync(absPath)) return null;
|
|
58
|
+
const raw = fs.readFileSync(absPath, "utf-8");
|
|
59
|
+
const { data, content } = matter(raw);
|
|
60
|
+
const typeName = inferType(vaultPath, relPath);
|
|
61
|
+
return { path: relPath, type: typeName, fields: data, body: content.trim() };
|
|
62
|
+
}
|
|
63
|
+
function list(vaultPath, typeName) {
|
|
64
|
+
const typeDef = getType(vaultPath, typeName);
|
|
65
|
+
if (!typeDef) return [];
|
|
66
|
+
const dir = path.join(vaultPath, typeDef.directory);
|
|
67
|
+
if (!fs.existsSync(dir)) return [];
|
|
68
|
+
const files = fs.readdirSync(dir).filter((f) => f.endsWith(".md"));
|
|
69
|
+
const instances = [];
|
|
70
|
+
for (const file of files) {
|
|
71
|
+
const relPath = `${typeDef.directory}/${file}`;
|
|
72
|
+
const inst = read(vaultPath, relPath);
|
|
73
|
+
if (inst) instances.push(inst);
|
|
74
|
+
}
|
|
75
|
+
return instances;
|
|
76
|
+
}
|
|
77
|
+
function update(vaultPath, relPath, fieldUpdates, bodyUpdate, actor) {
|
|
78
|
+
const existing = read(vaultPath, relPath);
|
|
79
|
+
if (!existing) throw new Error(`Not found: ${relPath}`);
|
|
80
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
81
|
+
const newFields = { ...existing.fields, ...fieldUpdates, updated: now };
|
|
82
|
+
const newBody = bodyUpdate ?? existing.body;
|
|
83
|
+
const absPath = path.join(vaultPath, relPath);
|
|
84
|
+
const content = matter.stringify(newBody, stripUndefined(newFields));
|
|
85
|
+
fs.writeFileSync(absPath, content, "utf-8");
|
|
86
|
+
append(vaultPath, actor, "update", relPath, existing.type, {
|
|
87
|
+
changed: Object.keys(fieldUpdates)
|
|
88
|
+
});
|
|
89
|
+
return { path: relPath, type: existing.type, fields: newFields, body: newBody };
|
|
90
|
+
}
|
|
91
|
+
function remove(vaultPath, relPath, actor) {
|
|
92
|
+
const absPath = path.join(vaultPath, relPath);
|
|
93
|
+
if (!fs.existsSync(absPath)) throw new Error(`Not found: ${relPath}`);
|
|
94
|
+
const archiveDir = path.join(vaultPath, ".clawvault", "archive");
|
|
95
|
+
if (!fs.existsSync(archiveDir)) fs.mkdirSync(archiveDir, { recursive: true });
|
|
96
|
+
const archivePath = path.join(archiveDir, path.basename(relPath));
|
|
97
|
+
fs.renameSync(absPath, archivePath);
|
|
98
|
+
const typeName = inferType(vaultPath, relPath);
|
|
99
|
+
append(vaultPath, actor, "delete", relPath, typeName);
|
|
100
|
+
}
|
|
101
|
+
function findByField(vaultPath, typeName, field, value) {
|
|
102
|
+
return list(vaultPath, typeName).filter((inst) => inst.fields[field] === value);
|
|
103
|
+
}
|
|
104
|
+
function openThreads(vaultPath) {
|
|
105
|
+
return findByField(vaultPath, "thread", "status", "open");
|
|
106
|
+
}
|
|
107
|
+
function activeThreads(vaultPath) {
|
|
108
|
+
return findByField(vaultPath, "thread", "status", "active");
|
|
109
|
+
}
|
|
110
|
+
function blockedThreads(vaultPath) {
|
|
111
|
+
return findByField(vaultPath, "thread", "status", "blocked");
|
|
112
|
+
}
|
|
113
|
+
function slugify(text) {
|
|
114
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 80);
|
|
115
|
+
}
|
|
116
|
+
function applyDefaults(typeDef, fields) {
|
|
117
|
+
const result = { ...fields };
|
|
118
|
+
for (const [key, def] of Object.entries(typeDef.fields)) {
|
|
119
|
+
if (result[key] === void 0 && def.default !== void 0) {
|
|
120
|
+
result[key] = def.default;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
function stripUndefined(obj) {
|
|
126
|
+
const result = {};
|
|
127
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
128
|
+
if (v !== void 0) result[k] = v;
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
function inferType(vaultPath, relPath) {
|
|
133
|
+
const registry = loadRegistry(vaultPath);
|
|
134
|
+
const dir = relPath.split("/")[0];
|
|
135
|
+
for (const typeDef of Object.values(registry.types)) {
|
|
136
|
+
if (typeDef.directory === dir) return typeDef.name;
|
|
137
|
+
}
|
|
138
|
+
return "unknown";
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export {
|
|
142
|
+
create,
|
|
143
|
+
read,
|
|
144
|
+
list,
|
|
145
|
+
update,
|
|
146
|
+
remove,
|
|
147
|
+
findByField,
|
|
148
|
+
openThreads,
|
|
149
|
+
activeThreads,
|
|
150
|
+
blockedThreads,
|
|
151
|
+
store_exports
|
|
152
|
+
};
|