clawvault 2.5.3 → 2.6.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 +159 -159
- package/bin/clawvault.js +111 -111
- package/bin/command-registration.test.js +166 -166
- package/bin/command-runtime.js +93 -93
- package/bin/command-runtime.test.js +154 -154
- package/bin/help-contract.test.js +39 -39
- package/bin/register-config-commands.js +153 -153
- package/bin/register-config-route-commands.test.js +121 -121
- package/bin/register-core-commands.js +237 -237
- package/bin/register-kanban-commands.js +56 -56
- package/bin/register-kanban-commands.test.js +83 -83
- package/bin/register-maintenance-commands.js +282 -282
- package/bin/register-project-commands.js +209 -209
- package/bin/register-project-commands.test.js +206 -206
- package/bin/register-query-commands.js +317 -317
- package/bin/register-query-commands.test.js +65 -65
- package/bin/register-resilience-commands.js +182 -182
- package/bin/register-resilience-commands.test.js +81 -81
- package/bin/register-route-commands.js +114 -114
- package/bin/register-session-lifecycle-commands.js +206 -206
- package/bin/register-tailscale-commands.js +106 -106
- package/bin/register-task-commands.js +348 -348
- package/bin/register-task-commands.test.js +69 -69
- package/bin/register-template-commands.js +75 -72
- package/bin/register-template-commands.test.js +87 -0
- package/bin/register-vault-operations-commands.js +300 -300
- package/bin/test-helpers/cli-command-fixtures.js +119 -119
- package/dashboard/lib/graph-diff.js +104 -104
- package/dashboard/lib/graph-diff.test.js +75 -75
- package/dashboard/lib/vault-parser.js +556 -556
- package/dashboard/lib/vault-parser.test.js +254 -254
- package/dashboard/public/app.js +796 -796
- package/dashboard/public/index.html +52 -52
- package/dashboard/public/styles.css +221 -221
- package/dashboard/server.js +374 -374
- package/dist/{chunk-J5EMBUPK.js → chunk-4OXMU5S2.js} +1 -1
- package/dist/{chunk-3FP5BJ42.js → chunk-4QYGFWRM.js} +1 -1
- package/dist/{chunk-4IV3R2F5.js → chunk-4TE4JMLA.js} +1 -1
- package/dist/{chunk-5GZFTAL7.js → chunk-AZYOKJYC.js} +128 -42
- package/dist/{chunk-FG6RJMCN.js → chunk-HA5M6KJB.js} +4 -4
- package/dist/{chunk-IZEY5S74.js → chunk-IEVLHNLU.js} +1 -1
- package/dist/{chunk-CLE2HHNT.js → chunk-IVRIKYFE.js} +18 -11
- package/dist/{chunk-AY4PGUVL.js → chunk-KL4NAOMO.js} +1 -1
- package/dist/{chunk-O7XHXF7F.js → chunk-MAKNAHAW.js} +4 -4
- package/dist/{chunk-OSMS7QIG.js → chunk-ME37YNW3.js} +2 -2
- package/dist/chunk-MFAWT5O5.js +301 -0
- package/dist/{chunk-TPDH3JPP.js → chunk-PBEE567J.js} +1 -1
- package/dist/{chunk-S2IG7VNM.js → chunk-Q2J5YTUF.js} +2 -2
- package/dist/{chunk-IOALNTAN.js → chunk-QWQ3TIKS.js} +103 -29
- package/dist/{chunk-YCVDVI5B.js → chunk-R2MIW5G7.js} +1 -1
- package/dist/{chunk-M25QVSJM.js → chunk-RVYA52PY.js} +1 -1
- package/dist/{chunk-NZ4ZZNSR.js → chunk-THRJVD4L.js} +1 -1
- package/dist/{chunk-4GBPTBFJ.js → chunk-TIGW564L.js} +1 -1
- package/dist/{chunk-LMEMZGUV.js → chunk-UEOUADMO.js} +3 -3
- package/dist/{chunk-GFJ3LIIB.js → chunk-XAVB4GB4.js} +1 -1
- package/dist/cli/index.js +15 -13
- package/dist/commands/backlog.js +3 -1
- package/dist/commands/blocked.js +3 -1
- package/dist/commands/canvas.js +3 -1
- package/dist/commands/context.js +3 -3
- package/dist/commands/doctor.js +9 -7
- package/dist/commands/embed.js +2 -2
- package/dist/commands/kanban.js +4 -2
- package/dist/commands/observe.js +7 -5
- package/dist/commands/project.js +5 -3
- package/dist/commands/rebuild.js +6 -4
- package/dist/commands/replay.js +6 -4
- package/dist/commands/setup.js +2 -2
- package/dist/commands/sleep.js +7 -5
- package/dist/commands/status.js +8 -6
- package/dist/commands/tailscale.js +3 -3
- package/dist/commands/task.js +4 -2
- package/dist/commands/template.d.ts +10 -1
- package/dist/commands/template.js +47 -55
- package/dist/commands/wake.js +2 -2
- package/dist/index.js +23 -22
- package/dist/lib/project-utils.js +4 -2
- package/dist/lib/tailscale.js +2 -2
- package/dist/lib/task-utils.d.ts +14 -13
- package/dist/lib/task-utils.js +3 -1
- package/dist/lib/template-engine.d.ts +1 -0
- package/dist/lib/webdav.js +1 -1
- package/hooks/clawvault/HOOK.md +83 -83
- package/hooks/clawvault/handler.js +816 -816
- package/hooks/clawvault/handler.test.js +263 -263
- package/package.json +94 -94
- package/templates/checkpoint.md +34 -19
- package/templates/daily-note.md +34 -19
- package/templates/daily.md +34 -19
- package/templates/decision.md +39 -17
- package/templates/handoff.md +34 -19
- package/templates/lesson.md +31 -16
- package/templates/person.md +37 -19
- package/templates/project.md +84 -23
- package/templates/task.md +81 -0
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
listTasks,
|
|
3
3
|
slugify
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-QWQ3TIKS.js";
|
|
5
|
+
import {
|
|
6
|
+
loadSchemaTemplateDefinition,
|
|
7
|
+
renderDocumentFromTemplate
|
|
8
|
+
} from "./chunk-MFAWT5O5.js";
|
|
5
9
|
|
|
6
10
|
// src/lib/project-utils.ts
|
|
7
11
|
import * as fs from "fs";
|
|
@@ -50,6 +54,103 @@ function normalizeProjectStatus(value) {
|
|
|
50
54
|
}
|
|
51
55
|
return "active";
|
|
52
56
|
}
|
|
57
|
+
function buildProjectFrontmatterFallback(now, options) {
|
|
58
|
+
const frontmatter = {
|
|
59
|
+
type: "project",
|
|
60
|
+
status: options.status ?? "active",
|
|
61
|
+
created: now,
|
|
62
|
+
updated: now
|
|
63
|
+
};
|
|
64
|
+
if (options.owner) frontmatter.owner = options.owner;
|
|
65
|
+
if (options.team && options.team.length > 0) {
|
|
66
|
+
const team = normalizeStringArray(options.team);
|
|
67
|
+
if (team.length > 0) frontmatter.team = team;
|
|
68
|
+
}
|
|
69
|
+
if (options.client) frontmatter.client = options.client;
|
|
70
|
+
if (options.tags && options.tags.length > 0) {
|
|
71
|
+
const tags = normalizeStringArray(options.tags);
|
|
72
|
+
if (tags.length > 0) frontmatter.tags = tags;
|
|
73
|
+
}
|
|
74
|
+
if (options.description) frontmatter.description = options.description;
|
|
75
|
+
if (options.started) frontmatter.started = options.started;
|
|
76
|
+
if (options.deadline) frontmatter.deadline = options.deadline;
|
|
77
|
+
if (options.repo) frontmatter.repo = options.repo;
|
|
78
|
+
if (options.url) frontmatter.url = options.url;
|
|
79
|
+
if (options.completed) frontmatter.completed = options.completed;
|
|
80
|
+
if (options.reason) frontmatter.reason = options.reason;
|
|
81
|
+
return frontmatter;
|
|
82
|
+
}
|
|
83
|
+
function buildProjectContentFallback(title, options) {
|
|
84
|
+
let content = `# ${title}
|
|
85
|
+
`;
|
|
86
|
+
const wikiLinks = /* @__PURE__ */ new Set();
|
|
87
|
+
if (options.owner) wikiLinks.add(options.owner);
|
|
88
|
+
if (options.client) wikiLinks.add(options.client);
|
|
89
|
+
for (const member of options.team || []) {
|
|
90
|
+
const trimmed = member.trim();
|
|
91
|
+
if (trimmed) wikiLinks.add(trimmed);
|
|
92
|
+
}
|
|
93
|
+
if (wikiLinks.size > 0) {
|
|
94
|
+
content += `
|
|
95
|
+
${Array.from(wikiLinks).map((link) => `[[${link}]]`).join(" | ")}
|
|
96
|
+
`;
|
|
97
|
+
}
|
|
98
|
+
if (options.content) {
|
|
99
|
+
content += `
|
|
100
|
+
${options.content}
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
return content;
|
|
104
|
+
}
|
|
105
|
+
function buildProjectTemplateOverrides(options) {
|
|
106
|
+
const overrides = {};
|
|
107
|
+
if (options.status) overrides.status = options.status;
|
|
108
|
+
if (options.owner) overrides.owner = options.owner;
|
|
109
|
+
if (options.team && options.team.length > 0) {
|
|
110
|
+
const team = normalizeStringArray(options.team);
|
|
111
|
+
if (team.length > 0) overrides.team = team;
|
|
112
|
+
}
|
|
113
|
+
if (options.client) overrides.client = options.client;
|
|
114
|
+
if (options.tags && options.tags.length > 0) {
|
|
115
|
+
const tags = normalizeStringArray(options.tags);
|
|
116
|
+
if (tags.length > 0) overrides.tags = tags;
|
|
117
|
+
}
|
|
118
|
+
if (options.description) overrides.description = options.description;
|
|
119
|
+
if (options.started) overrides.started = options.started;
|
|
120
|
+
if (options.deadline) overrides.deadline = options.deadline;
|
|
121
|
+
if (options.repo) overrides.repo = options.repo;
|
|
122
|
+
if (options.url) overrides.url = options.url;
|
|
123
|
+
if (options.completed) overrides.completed = options.completed;
|
|
124
|
+
if (options.reason) overrides.reason = options.reason;
|
|
125
|
+
return overrides;
|
|
126
|
+
}
|
|
127
|
+
function buildProjectTemplateVariables(title, slug, options) {
|
|
128
|
+
const ownerLink = options.owner ? `[[${options.owner}]]` : "";
|
|
129
|
+
const clientLink = options.client ? `[[${options.client}]]` : "";
|
|
130
|
+
const teamLinks = (options.team || []).map((member) => member.trim()).filter(Boolean).map((member) => `[[${member}]]`);
|
|
131
|
+
const linksLine = [ownerLink, clientLink, ...teamLinks].filter(Boolean).join(" | ");
|
|
132
|
+
return {
|
|
133
|
+
title,
|
|
134
|
+
slug,
|
|
135
|
+
status: options.status ?? "",
|
|
136
|
+
owner: options.owner ?? "",
|
|
137
|
+
client: options.client ?? "",
|
|
138
|
+
team_csv: (options.team || []).join(", "),
|
|
139
|
+
tags_csv: (options.tags || []).join(", "),
|
|
140
|
+
description: options.description ?? "",
|
|
141
|
+
started: options.started ?? "",
|
|
142
|
+
deadline: options.deadline ?? "",
|
|
143
|
+
repo: options.repo ?? "",
|
|
144
|
+
url: options.url ?? "",
|
|
145
|
+
completed: options.completed ?? "",
|
|
146
|
+
reason: options.reason ?? "",
|
|
147
|
+
content: options.content ?? "",
|
|
148
|
+
owner_link: ownerLink,
|
|
149
|
+
client_link: clientLink,
|
|
150
|
+
team_links_line: teamLinks.join(" | "),
|
|
151
|
+
links_line: linksLine
|
|
152
|
+
};
|
|
153
|
+
}
|
|
53
154
|
function normalizeProjectFrontmatter(frontmatter) {
|
|
54
155
|
const normalizedCreated = typeof frontmatter.created === "string" && frontmatter.created ? frontmatter.created : (/* @__PURE__ */ new Date(0)).toISOString();
|
|
55
156
|
const normalizedUpdated = typeof frontmatter.updated === "string" && frontmatter.updated ? frontmatter.updated : normalizedCreated;
|
|
@@ -147,47 +248,32 @@ function createProject(vaultPath, title, options = {}) {
|
|
|
147
248
|
throw new Error(`Project already exists: ${slug}`);
|
|
148
249
|
}
|
|
149
250
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (options.owner) wikiLinks.add(options.owner);
|
|
177
|
-
if (options.client) wikiLinks.add(options.client);
|
|
178
|
-
for (const member of options.team || []) {
|
|
179
|
-
const trimmed = member.trim();
|
|
180
|
-
if (trimmed) wikiLinks.add(trimmed);
|
|
181
|
-
}
|
|
182
|
-
if (wikiLinks.size > 0) {
|
|
183
|
-
content += `
|
|
184
|
-
${Array.from(wikiLinks).map((link) => `[[${link}]]`).join(" | ")}
|
|
185
|
-
`;
|
|
186
|
-
}
|
|
187
|
-
if (options.content) {
|
|
188
|
-
content += `
|
|
189
|
-
${options.content}
|
|
190
|
-
`;
|
|
251
|
+
const template = loadSchemaTemplateDefinition("project", {
|
|
252
|
+
vaultPath: path.resolve(vaultPath)
|
|
253
|
+
});
|
|
254
|
+
let frontmatter;
|
|
255
|
+
let content;
|
|
256
|
+
if (template) {
|
|
257
|
+
const rendered = renderDocumentFromTemplate(template, {
|
|
258
|
+
title,
|
|
259
|
+
type: "project",
|
|
260
|
+
now: new Date(now),
|
|
261
|
+
variables: buildProjectTemplateVariables(title, slug, options),
|
|
262
|
+
overrides: buildProjectTemplateOverrides(options),
|
|
263
|
+
frontmatter: { pruneEmpty: true }
|
|
264
|
+
});
|
|
265
|
+
const templateFrontmatter = rendered.frontmatter;
|
|
266
|
+
frontmatter = normalizeProjectFrontmatter({
|
|
267
|
+
...templateFrontmatter,
|
|
268
|
+
type: "project",
|
|
269
|
+
status: normalizeProjectStatus(templateFrontmatter.status),
|
|
270
|
+
created: typeof templateFrontmatter.created === "string" && templateFrontmatter.created ? templateFrontmatter.created : now,
|
|
271
|
+
updated: typeof templateFrontmatter.updated === "string" && templateFrontmatter.updated ? templateFrontmatter.updated : now
|
|
272
|
+
});
|
|
273
|
+
content = rendered.content;
|
|
274
|
+
} else {
|
|
275
|
+
frontmatter = buildProjectFrontmatterFallback(now, options);
|
|
276
|
+
content = buildProjectContentFallback(title, options);
|
|
191
277
|
}
|
|
192
278
|
const fileContent = matter.stringify(content, frontmatter);
|
|
193
279
|
fs.writeFileSync(projectPath, fileContent);
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
registerTailscaleCommands
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-THRJVD4L.js";
|
|
4
4
|
import {
|
|
5
5
|
registerObserveCommand
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ME37YNW3.js";
|
|
7
7
|
import {
|
|
8
8
|
registerReflectCommand
|
|
9
9
|
} from "./chunk-2YDBJS7M.js";
|
|
10
10
|
import {
|
|
11
11
|
registerContextCommand
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-XAVB4GB4.js";
|
|
13
13
|
import {
|
|
14
14
|
registerEmbedCommand
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-4QYGFWRM.js";
|
|
16
16
|
import {
|
|
17
17
|
registerInjectCommand
|
|
18
18
|
} from "./chunk-GSD4ALSI.js";
|
|
@@ -9,17 +9,24 @@ var BLOCKED_PATHS = [
|
|
|
9
9
|
"node_modules"
|
|
10
10
|
];
|
|
11
11
|
var SUPPORTED_METHODS = ["GET", "PUT", "DELETE", "MKCOL", "PROPFIND", "OPTIONS", "HEAD", "MOVE", "COPY"];
|
|
12
|
+
function toRequestSegments(requestPath) {
|
|
13
|
+
return requestPath.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
14
|
+
}
|
|
15
|
+
function isWithinRoot(fullPath, rootPath) {
|
|
16
|
+
const resolvedRoot = path.resolve(rootPath);
|
|
17
|
+
const relative2 = path.relative(resolvedRoot, fullPath);
|
|
18
|
+
return !(relative2.startsWith("..") || path.isAbsolute(relative2));
|
|
19
|
+
}
|
|
12
20
|
function isPathSafe(requestPath, rootPath) {
|
|
13
|
-
|
|
21
|
+
const pathParts = toRequestSegments(requestPath);
|
|
22
|
+
if (pathParts.includes("..")) {
|
|
14
23
|
return false;
|
|
15
24
|
}
|
|
16
|
-
const
|
|
17
|
-
const fullPath = path.resolve(rootPath,
|
|
18
|
-
|
|
19
|
-
if (!fullPath.startsWith(resolvedRoot + path.sep) && fullPath !== resolvedRoot) {
|
|
25
|
+
const normalizedRelativePath = path.normalize(pathParts.join(path.sep));
|
|
26
|
+
const fullPath = path.resolve(rootPath, normalizedRelativePath);
|
|
27
|
+
if (!isWithinRoot(fullPath, rootPath)) {
|
|
20
28
|
return false;
|
|
21
29
|
}
|
|
22
|
-
const pathParts = normalizedPath.split(path.sep);
|
|
23
30
|
for (const part of pathParts) {
|
|
24
31
|
if (BLOCKED_PATHS.includes(part)) {
|
|
25
32
|
return false;
|
|
@@ -28,13 +35,13 @@ function isPathSafe(requestPath, rootPath) {
|
|
|
28
35
|
return true;
|
|
29
36
|
}
|
|
30
37
|
function resolveWebDAVPath(requestPath, rootPath) {
|
|
31
|
-
|
|
38
|
+
const pathParts = toRequestSegments(requestPath);
|
|
39
|
+
if (pathParts.includes("..")) {
|
|
32
40
|
return null;
|
|
33
41
|
}
|
|
34
|
-
const
|
|
35
|
-
const fullPath = path.resolve(rootPath,
|
|
36
|
-
|
|
37
|
-
if (!fullPath.startsWith(resolvedRoot + path.sep) && fullPath !== resolvedRoot) {
|
|
42
|
+
const normalizedRelativePath = path.normalize(pathParts.join(path.sep));
|
|
43
|
+
const fullPath = path.resolve(rootPath, normalizedRelativePath);
|
|
44
|
+
if (!isWithinRoot(fullPath, rootPath)) {
|
|
38
45
|
return null;
|
|
39
46
|
}
|
|
40
47
|
return fullPath;
|
|
@@ -235,14 +235,14 @@ var SearchEngine = class {
|
|
|
235
235
|
for (const qr of qmdResults) {
|
|
236
236
|
const filePath = this.qmdUriToPath(qr.file);
|
|
237
237
|
const relativePath = this.vaultPath ? path.relative(this.vaultPath, filePath) : filePath;
|
|
238
|
-
const normalizedRelativePath = relativePath.
|
|
238
|
+
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
239
239
|
if (normalizedRelativePath.startsWith("ledger/archive/") || normalizedRelativePath.includes("/ledger/archive/")) {
|
|
240
240
|
continue;
|
|
241
241
|
}
|
|
242
|
-
const docId =
|
|
243
|
-
let doc = this.documents.get(docId);
|
|
242
|
+
const docId = normalizedRelativePath.replace(/\.md$/, "");
|
|
243
|
+
let doc = this.documents.get(docId) ?? this.documents.get(docId.split("/").join(path.sep));
|
|
244
244
|
const modifiedAt = this.resolveModifiedAt(doc, filePath);
|
|
245
|
-
const parts =
|
|
245
|
+
const parts = normalizedRelativePath.split("/");
|
|
246
246
|
const docCategory = parts.length > 1 ? parts[0] : "root";
|
|
247
247
|
if (category && docCategory !== category) continue;
|
|
248
248
|
if (tags && tags.length > 0 && doc) {
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
} from "./chunk-P5EPF6MB.js";
|
|
4
4
|
import {
|
|
5
5
|
observeActiveSessions
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-IEVLHNLU.js";
|
|
7
7
|
import {
|
|
8
8
|
Observer
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-Q2J5YTUF.js";
|
|
10
10
|
import {
|
|
11
11
|
resolveVaultPath
|
|
12
12
|
} from "./chunk-MXSSG3QU.js";
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildTemplateVariables,
|
|
3
|
+
renderTemplate
|
|
4
|
+
} from "./chunk-7766SIJP.js";
|
|
5
|
+
|
|
6
|
+
// src/lib/primitive-templates.ts
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
import matter from "gray-matter";
|
|
11
|
+
var TEMPLATE_EXTENSION = ".md";
|
|
12
|
+
function isRecord(value) {
|
|
13
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
|
+
}
|
|
15
|
+
function normalizeTemplateName(name) {
|
|
16
|
+
const base = path.basename(name, path.extname(name));
|
|
17
|
+
return base.trim();
|
|
18
|
+
}
|
|
19
|
+
function resolveBuiltinTemplatesDir(override) {
|
|
20
|
+
if (override) {
|
|
21
|
+
const resolved = path.resolve(override);
|
|
22
|
+
return fs.existsSync(resolved) && fs.statSync(resolved).isDirectory() ? resolved : null;
|
|
23
|
+
}
|
|
24
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
const candidates = [
|
|
26
|
+
path.resolve(moduleDir, "../templates"),
|
|
27
|
+
path.resolve(moduleDir, "../../templates")
|
|
28
|
+
];
|
|
29
|
+
for (const candidate of candidates) {
|
|
30
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
|
|
31
|
+
return candidate;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
function listTemplateFiles(dir, ignore) {
|
|
37
|
+
const entries = /* @__PURE__ */ new Map();
|
|
38
|
+
if (!fs.existsSync(dir)) return entries;
|
|
39
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
40
|
+
if (!entry.isFile() || !entry.name.endsWith(TEMPLATE_EXTENSION)) continue;
|
|
41
|
+
const name = normalizeTemplateName(entry.name);
|
|
42
|
+
if (!name) continue;
|
|
43
|
+
if (ignore?.has(name)) continue;
|
|
44
|
+
entries.set(name, path.join(dir, entry.name));
|
|
45
|
+
}
|
|
46
|
+
return entries;
|
|
47
|
+
}
|
|
48
|
+
function buildTemplateIndex(options = {}) {
|
|
49
|
+
const index = /* @__PURE__ */ new Map();
|
|
50
|
+
const builtinDir = resolveBuiltinTemplatesDir(options.builtinDir);
|
|
51
|
+
if (builtinDir) {
|
|
52
|
+
for (const [name, filePath] of listTemplateFiles(builtinDir, options.ignoreBuiltinNames)) {
|
|
53
|
+
index.set(name, filePath);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (options.vaultPath) {
|
|
57
|
+
const vaultTemplatesDir = path.join(path.resolve(options.vaultPath), "templates");
|
|
58
|
+
for (const [name, filePath] of listTemplateFiles(vaultTemplatesDir)) {
|
|
59
|
+
index.set(name, filePath);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return index;
|
|
63
|
+
}
|
|
64
|
+
function inferFieldType(defaultValue) {
|
|
65
|
+
if (Array.isArray(defaultValue)) {
|
|
66
|
+
const uniqueItemTypes = [...new Set(defaultValue.map((value) => typeof value))];
|
|
67
|
+
if (uniqueItemTypes.length === 1 && uniqueItemTypes[0] === "string") {
|
|
68
|
+
return "string[]";
|
|
69
|
+
}
|
|
70
|
+
return "array";
|
|
71
|
+
}
|
|
72
|
+
switch (typeof defaultValue) {
|
|
73
|
+
case "string":
|
|
74
|
+
return "string";
|
|
75
|
+
case "number":
|
|
76
|
+
return "number";
|
|
77
|
+
case "boolean":
|
|
78
|
+
return "boolean";
|
|
79
|
+
case "object":
|
|
80
|
+
if (defaultValue === null) return "string";
|
|
81
|
+
return "object";
|
|
82
|
+
default:
|
|
83
|
+
return "string";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function normalizeFieldDefinition(rawField) {
|
|
87
|
+
if (!isRecord(rawField)) {
|
|
88
|
+
return {
|
|
89
|
+
type: inferFieldType(rawField),
|
|
90
|
+
default: rawField
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const rawType = typeof rawField.type === "string" ? rawField.type.trim() : "";
|
|
94
|
+
const normalized = {
|
|
95
|
+
type: rawType || inferFieldType(rawField.default)
|
|
96
|
+
};
|
|
97
|
+
if (typeof rawField.description === "string" && rawField.description.trim()) {
|
|
98
|
+
normalized.description = rawField.description.trim();
|
|
99
|
+
}
|
|
100
|
+
if (typeof rawField.required === "boolean") {
|
|
101
|
+
normalized.required = rawField.required;
|
|
102
|
+
}
|
|
103
|
+
if (Object.prototype.hasOwnProperty.call(rawField, "default")) {
|
|
104
|
+
normalized.default = rawField.default;
|
|
105
|
+
}
|
|
106
|
+
if (Array.isArray(rawField.enum)) {
|
|
107
|
+
normalized.enum = rawField.enum;
|
|
108
|
+
}
|
|
109
|
+
return normalized;
|
|
110
|
+
}
|
|
111
|
+
function normalizeFieldDefinitions(rawFields) {
|
|
112
|
+
const normalized = {};
|
|
113
|
+
for (const [fieldName, rawField] of Object.entries(rawFields)) {
|
|
114
|
+
const normalizedName = String(fieldName).trim();
|
|
115
|
+
if (!normalizedName) continue;
|
|
116
|
+
normalized[normalizedName] = normalizeFieldDefinition(rawField);
|
|
117
|
+
}
|
|
118
|
+
return normalized;
|
|
119
|
+
}
|
|
120
|
+
function extractSchemaDefinition(frontmatter) {
|
|
121
|
+
const primitive = typeof frontmatter.primitive === "string" ? frontmatter.primitive.trim() : "";
|
|
122
|
+
const description = typeof frontmatter.description === "string" ? frontmatter.description.trim() : void 0;
|
|
123
|
+
if (primitive && isRecord(frontmatter.fields)) {
|
|
124
|
+
return {
|
|
125
|
+
primitive,
|
|
126
|
+
description,
|
|
127
|
+
fields: frontmatter.fields
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const containerCandidates = [frontmatter.schema, frontmatter.template];
|
|
131
|
+
for (const candidate of containerCandidates) {
|
|
132
|
+
if (!isRecord(candidate)) continue;
|
|
133
|
+
const nestedPrimitive = typeof candidate.primitive === "string" ? candidate.primitive.trim() : primitive;
|
|
134
|
+
if (!nestedPrimitive || !isRecord(candidate.fields)) continue;
|
|
135
|
+
const nestedDescription = typeof candidate.description === "string" ? candidate.description.trim() : description;
|
|
136
|
+
return {
|
|
137
|
+
primitive: nestedPrimitive,
|
|
138
|
+
description: nestedDescription,
|
|
139
|
+
fields: candidate.fields
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
function inferLegacyFieldDefinitions(frontmatter) {
|
|
145
|
+
const normalized = {};
|
|
146
|
+
const ignoredKeys = /* @__PURE__ */ new Set(["primitive", "fields", "schema", "template"]);
|
|
147
|
+
for (const [key, value] of Object.entries(frontmatter)) {
|
|
148
|
+
if (ignoredKeys.has(key)) continue;
|
|
149
|
+
normalized[key] = {
|
|
150
|
+
type: inferFieldType(value),
|
|
151
|
+
default: value
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return normalized;
|
|
155
|
+
}
|
|
156
|
+
function parseTemplateDefinition(rawTemplate, templateName, sourcePath) {
|
|
157
|
+
const normalizedName = normalizeTemplateName(templateName);
|
|
158
|
+
const { data, content } = matter(rawTemplate);
|
|
159
|
+
const frontmatter = isRecord(data) ? data : {};
|
|
160
|
+
const extractedSchema = extractSchemaDefinition(frontmatter);
|
|
161
|
+
if (extractedSchema) {
|
|
162
|
+
return {
|
|
163
|
+
name: normalizedName,
|
|
164
|
+
primitive: extractedSchema.primitive,
|
|
165
|
+
description: extractedSchema.description,
|
|
166
|
+
fields: normalizeFieldDefinitions(extractedSchema.fields),
|
|
167
|
+
body: content,
|
|
168
|
+
format: "schema",
|
|
169
|
+
sourcePath
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
name: normalizedName,
|
|
174
|
+
primitive: normalizedName,
|
|
175
|
+
description: typeof frontmatter.description === "string" ? frontmatter.description.trim() : void 0,
|
|
176
|
+
fields: inferLegacyFieldDefinitions(frontmatter),
|
|
177
|
+
body: content,
|
|
178
|
+
format: "legacy",
|
|
179
|
+
sourcePath
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function readTemplateDefinitionFromPath(filePath, templateName) {
|
|
183
|
+
try {
|
|
184
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
185
|
+
return parseTemplateDefinition(raw, templateName, filePath);
|
|
186
|
+
} catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function loadTemplateDefinition(templateName, options = {}) {
|
|
191
|
+
const normalizedName = normalizeTemplateName(templateName);
|
|
192
|
+
if (!normalizedName) return null;
|
|
193
|
+
const index = buildTemplateIndex(options);
|
|
194
|
+
const filePath = index.get(normalizedName);
|
|
195
|
+
if (!filePath) return null;
|
|
196
|
+
return readTemplateDefinitionFromPath(filePath, normalizedName);
|
|
197
|
+
}
|
|
198
|
+
function loadSchemaTemplateDefinition(templateName, options = {}) {
|
|
199
|
+
const definition = loadTemplateDefinition(templateName, options);
|
|
200
|
+
if (!definition || definition.format !== "schema") {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
return definition;
|
|
204
|
+
}
|
|
205
|
+
function listTemplateDefinitions(options = {}) {
|
|
206
|
+
const index = buildTemplateIndex(options);
|
|
207
|
+
const entries = [];
|
|
208
|
+
for (const [name, filePath] of [...index.entries()].sort(([left], [right]) => left.localeCompare(right))) {
|
|
209
|
+
const definition = readTemplateDefinitionFromPath(filePath, name);
|
|
210
|
+
if (!definition) continue;
|
|
211
|
+
entries.push({
|
|
212
|
+
...definition,
|
|
213
|
+
path: filePath
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return entries;
|
|
217
|
+
}
|
|
218
|
+
function resolveInterpolatedValue(value, variables) {
|
|
219
|
+
if (typeof value === "string") {
|
|
220
|
+
return renderTemplate(value, variables);
|
|
221
|
+
}
|
|
222
|
+
if (Array.isArray(value)) {
|
|
223
|
+
return value.map((item) => resolveInterpolatedValue(item, variables));
|
|
224
|
+
}
|
|
225
|
+
if (isRecord(value)) {
|
|
226
|
+
const resolved = {};
|
|
227
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
228
|
+
resolved[key] = resolveInterpolatedValue(nested, variables);
|
|
229
|
+
}
|
|
230
|
+
return resolved;
|
|
231
|
+
}
|
|
232
|
+
return value;
|
|
233
|
+
}
|
|
234
|
+
function pruneFrontmatter(frontmatter, options) {
|
|
235
|
+
const dropEmptyStrings = options.dropEmptyStrings ?? true;
|
|
236
|
+
const dropEmptyArrays = options.dropEmptyArrays ?? true;
|
|
237
|
+
const pruned = {};
|
|
238
|
+
for (const [key, value] of Object.entries(frontmatter)) {
|
|
239
|
+
if (value === void 0 || value === null) continue;
|
|
240
|
+
if (dropEmptyStrings && typeof value === "string" && value.trim() === "") continue;
|
|
241
|
+
if (dropEmptyArrays && Array.isArray(value) && value.length === 0) continue;
|
|
242
|
+
pruned[key] = value;
|
|
243
|
+
}
|
|
244
|
+
return pruned;
|
|
245
|
+
}
|
|
246
|
+
function buildFrontmatterFromTemplate(definition, variables, overrides = {}, options = {}) {
|
|
247
|
+
const frontmatter = {};
|
|
248
|
+
for (const [fieldName, schema] of Object.entries(definition.fields)) {
|
|
249
|
+
if (!Object.prototype.hasOwnProperty.call(schema, "default")) continue;
|
|
250
|
+
frontmatter[fieldName] = resolveInterpolatedValue(schema.default, variables);
|
|
251
|
+
}
|
|
252
|
+
for (const [fieldName, value] of Object.entries(overrides)) {
|
|
253
|
+
if (value === void 0) continue;
|
|
254
|
+
if (value === null) {
|
|
255
|
+
delete frontmatter[fieldName];
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
frontmatter[fieldName] = value;
|
|
259
|
+
}
|
|
260
|
+
if (!options.pruneEmpty) {
|
|
261
|
+
return frontmatter;
|
|
262
|
+
}
|
|
263
|
+
return pruneFrontmatter(frontmatter, options);
|
|
264
|
+
}
|
|
265
|
+
function renderDocumentFromTemplate(definition, options = {}) {
|
|
266
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
267
|
+
const variables = {
|
|
268
|
+
...buildTemplateVariables(
|
|
269
|
+
{
|
|
270
|
+
title: options.title ?? "",
|
|
271
|
+
type: options.type ?? definition.primitive
|
|
272
|
+
},
|
|
273
|
+
now
|
|
274
|
+
),
|
|
275
|
+
...options.variables ?? {}
|
|
276
|
+
};
|
|
277
|
+
const frontmatter = buildFrontmatterFromTemplate(
|
|
278
|
+
definition,
|
|
279
|
+
variables,
|
|
280
|
+
options.overrides,
|
|
281
|
+
options.frontmatter
|
|
282
|
+
);
|
|
283
|
+
const content = renderTemplate(definition.body, variables);
|
|
284
|
+
const markdown = matter.stringify(content, frontmatter);
|
|
285
|
+
return {
|
|
286
|
+
frontmatter,
|
|
287
|
+
content,
|
|
288
|
+
markdown,
|
|
289
|
+
variables
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export {
|
|
294
|
+
TEMPLATE_EXTENSION,
|
|
295
|
+
normalizeTemplateName,
|
|
296
|
+
buildTemplateIndex,
|
|
297
|
+
parseTemplateDefinition,
|
|
298
|
+
loadSchemaTemplateDefinition,
|
|
299
|
+
listTemplateDefinitions,
|
|
300
|
+
renderDocumentFromTemplate
|
|
301
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
listProjects
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-AZYOKJYC.js";
|
|
4
4
|
import {
|
|
5
5
|
DATE_HEADING_RE,
|
|
6
6
|
inferObservationType,
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
listTasks,
|
|
30
30
|
updateBacklogItem,
|
|
31
31
|
updateTask
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-QWQ3TIKS.js";
|
|
33
33
|
|
|
34
34
|
// src/observer/compressor.ts
|
|
35
35
|
var OPENAI_BASE_URL = "https://api.openai.com/v1";
|