clawvault 2.5.4 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +159 -159
  2. package/bin/clawvault.js +111 -111
  3. package/bin/command-registration.test.js +166 -166
  4. package/bin/command-runtime.js +93 -93
  5. package/bin/command-runtime.test.js +154 -154
  6. package/bin/help-contract.test.js +39 -39
  7. package/bin/register-config-commands.js +153 -153
  8. package/bin/register-config-route-commands.test.js +121 -121
  9. package/bin/register-core-commands.js +237 -237
  10. package/bin/register-kanban-commands.js +56 -56
  11. package/bin/register-kanban-commands.test.js +83 -83
  12. package/bin/register-maintenance-commands.js +282 -282
  13. package/bin/register-project-commands.js +209 -209
  14. package/bin/register-project-commands.test.js +206 -206
  15. package/bin/register-query-commands.js +317 -317
  16. package/bin/register-query-commands.test.js +65 -65
  17. package/bin/register-resilience-commands.js +182 -182
  18. package/bin/register-resilience-commands.test.js +81 -81
  19. package/bin/register-route-commands.js +114 -114
  20. package/bin/register-session-lifecycle-commands.js +206 -206
  21. package/bin/register-tailscale-commands.js +106 -106
  22. package/bin/register-task-commands.js +348 -348
  23. package/bin/register-task-commands.test.js +69 -69
  24. package/bin/register-template-commands.js +75 -72
  25. package/bin/register-template-commands.test.js +87 -0
  26. package/bin/register-vault-operations-commands.js +300 -300
  27. package/bin/test-helpers/cli-command-fixtures.js +119 -119
  28. package/dashboard/lib/graph-diff.js +104 -104
  29. package/dashboard/lib/graph-diff.test.js +75 -75
  30. package/dashboard/lib/vault-parser.js +556 -556
  31. package/dashboard/lib/vault-parser.test.js +254 -254
  32. package/dashboard/public/app.js +796 -796
  33. package/dashboard/public/index.html +52 -52
  34. package/dashboard/public/styles.css +221 -221
  35. package/dashboard/server.js +374 -374
  36. package/dist/{chunk-2YDBJS7M.js → chunk-3BTHWPMB.js} +1 -1
  37. package/dist/{chunk-J5EMBUPK.js → chunk-4OXMU5S2.js} +1 -1
  38. package/dist/{chunk-GSD4ALSI.js → chunk-4VRIMU4O.js} +1 -1
  39. package/dist/{chunk-5GZFTAL7.js → chunk-AZYOKJYC.js} +128 -42
  40. package/dist/{chunk-K3CDT7IH.js → chunk-HIHOUSXS.js} +2 -2
  41. package/dist/{chunk-IZEY5S74.js → chunk-IEVLHNLU.js} +1 -1
  42. package/dist/{chunk-OSMS7QIG.js → chunk-ME37YNW3.js} +2 -2
  43. package/dist/chunk-MFAWT5O5.js +301 -0
  44. package/dist/{chunk-TPDH3JPP.js → chunk-PBEE567J.js} +1 -1
  45. package/dist/{chunk-S2IG7VNM.js → chunk-Q2J5YTUF.js} +2 -2
  46. package/dist/{chunk-IOALNTAN.js → chunk-QWQ3TIKS.js} +103 -29
  47. package/dist/{chunk-YCVDVI5B.js → chunk-R2MIW5G7.js} +1 -1
  48. package/dist/{chunk-4IV3R2F5.js → chunk-R6SXNSFD.js} +2 -2
  49. package/dist/{chunk-YOSEUUNB.js → chunk-T76H47ZS.js} +1 -1
  50. package/dist/{chunk-JDLOL2PL.js → chunk-TLGBDTYT.js} +3 -3
  51. package/dist/{chunk-W2HNZC22.js → chunk-UEOUADMO.js} +1 -1
  52. package/dist/cli/index.js +12 -10
  53. package/dist/commands/backlog.js +3 -1
  54. package/dist/commands/blocked.js +3 -1
  55. package/dist/commands/canvas.js +3 -1
  56. package/dist/commands/doctor.js +7 -5
  57. package/dist/commands/inject.js +2 -2
  58. package/dist/commands/kanban.js +4 -2
  59. package/dist/commands/observe.js +7 -5
  60. package/dist/commands/project.js +5 -3
  61. package/dist/commands/rebuild.js +6 -4
  62. package/dist/commands/reflect.js +3 -3
  63. package/dist/commands/replay.js +8 -6
  64. package/dist/commands/setup.js +1 -1
  65. package/dist/commands/sleep.js +7 -5
  66. package/dist/commands/status.js +6 -4
  67. package/dist/commands/task.js +4 -2
  68. package/dist/commands/template.d.ts +10 -1
  69. package/dist/commands/template.js +47 -55
  70. package/dist/commands/wake.js +2 -2
  71. package/dist/index.js +20 -19
  72. package/dist/lib/project-utils.js +4 -2
  73. package/dist/lib/task-utils.d.ts +14 -13
  74. package/dist/lib/task-utils.js +3 -1
  75. package/dist/lib/template-engine.d.ts +1 -0
  76. package/hooks/clawvault/HOOK.md +83 -83
  77. package/hooks/clawvault/handler.js +816 -816
  78. package/hooks/clawvault/handler.test.js +263 -263
  79. package/package.json +94 -94
  80. package/templates/checkpoint.md +34 -19
  81. package/templates/daily-note.md +34 -19
  82. package/templates/daily.md +34 -19
  83. package/templates/decision.md +39 -17
  84. package/templates/handoff.md +34 -19
  85. package/templates/lesson.md +31 -16
  86. package/templates/person.md +37 -19
  87. package/templates/project.md +84 -23
  88. package/templates/task.md +81 -0
  89. /package/dist/{chunk-AXKYDCNN.js → chunk-RVYA52PY.js} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runReflection
3
- } from "./chunk-YOSEUUNB.js";
3
+ } from "./chunk-T76H47ZS.js";
4
4
  import {
5
5
  resolveVaultPath
6
6
  } from "./chunk-MXSSG3QU.js";
@@ -2,7 +2,7 @@ import {
2
2
  listTasks,
3
3
  readTask,
4
4
  updateTask
5
- } from "./chunk-IOALNTAN.js";
5
+ } from "./chunk-QWQ3TIKS.js";
6
6
 
7
7
  // src/commands/kanban.ts
8
8
  import * as fs from "fs";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  requestLlmCompletion,
3
3
  resolveLlmProvider
4
- } from "./chunk-K3CDT7IH.js";
4
+ } from "./chunk-HIHOUSXS.js";
5
5
  import {
6
6
  listConfig
7
7
  } from "./chunk-ITPEXLHA.js";
@@ -1,7 +1,11 @@
1
1
  import {
2
2
  listTasks,
3
3
  slugify
4
- } from "./chunk-IOALNTAN.js";
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 frontmatter = {
151
- type: "project",
152
- status: options.status ?? "active",
153
- created: now,
154
- updated: now
155
- };
156
- if (options.owner) frontmatter.owner = options.owner;
157
- if (options.team && options.team.length > 0) {
158
- const team = normalizeStringArray(options.team);
159
- if (team.length > 0) frontmatter.team = team;
160
- }
161
- if (options.client) frontmatter.client = options.client;
162
- if (options.tags && options.tags.length > 0) {
163
- const tags = normalizeStringArray(options.tags);
164
- if (tags.length > 0) frontmatter.tags = tags;
165
- }
166
- if (options.description) frontmatter.description = options.description;
167
- if (options.started) frontmatter.started = options.started;
168
- if (options.deadline) frontmatter.deadline = options.deadline;
169
- if (options.repo) frontmatter.repo = options.repo;
170
- if (options.url) frontmatter.url = options.url;
171
- if (options.completed) frontmatter.completed = options.completed;
172
- if (options.reason) frontmatter.reason = options.reason;
173
- let content = `# ${title}
174
- `;
175
- const wikiLinks = /* @__PURE__ */ new Set();
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);
@@ -96,10 +96,10 @@ async function callGemini(options, provider) {
96
96
  const fetchImpl = options.fetchImpl ?? fetch;
97
97
  const model = options.model ?? DEFAULT_MODELS[provider];
98
98
  const response = await fetchImpl(
99
- `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`,
99
+ `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent`,
100
100
  {
101
101
  method: "POST",
102
- headers: { "content-type": "application/json" },
102
+ headers: { "content-type": "application/json", "x-goog-api-key": apiKey },
103
103
  body: JSON.stringify({
104
104
  contents: [{ parts: [{ text: options.prompt }] }],
105
105
  generationConfig: {
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-HRLWZGMA.js";
4
4
  import {
5
5
  Observer
6
- } from "./chunk-S2IG7VNM.js";
6
+ } from "./chunk-Q2J5YTUF.js";
7
7
 
8
8
  // src/observer/active-session-observer.ts
9
9
  import * as fs from "fs";
@@ -3,10 +3,10 @@ import {
3
3
  } from "./chunk-P5EPF6MB.js";
4
4
  import {
5
5
  observeActiveSessions
6
- } from "./chunk-IZEY5S74.js";
6
+ } from "./chunk-IEVLHNLU.js";
7
7
  import {
8
8
  Observer
9
- } from "./chunk-S2IG7VNM.js";
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
  Observer
3
- } from "./chunk-S2IG7VNM.js";
3
+ } from "./chunk-Q2J5YTUF.js";
4
4
  import {
5
5
  resolveVaultPath
6
6
  } from "./chunk-MXSSG3QU.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  listProjects
3
- } from "./chunk-5GZFTAL7.js";
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-IOALNTAN.js";
32
+ } from "./chunk-QWQ3TIKS.js";
33
33
 
34
34
  // src/observer/compressor.ts
35
35
  var OPENAI_BASE_URL = "https://api.openai.com/v1";