clawvault 2.1.2 → 2.2.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.
Files changed (54) hide show
  1. package/bin/command-registration.test.js +6 -1
  2. package/bin/help-contract.test.js +2 -0
  3. package/bin/register-maintenance-commands.js +111 -0
  4. package/bin/register-query-commands.js +32 -1
  5. package/bin/register-session-lifecycle-commands.js +2 -0
  6. package/dist/{chunk-5MQB7B37.js → chunk-2HM7ZI4X.js} +268 -434
  7. package/dist/{chunk-TBVI4N53.js → chunk-6RIHODNR.js} +120 -95
  8. package/dist/chunk-73P7XCQM.js +104 -0
  9. package/dist/{chunk-MIIXBNO3.js → chunk-DHBDH4DN.js} +4 -0
  10. package/dist/chunk-GJEGPO7U.js +49 -0
  11. package/dist/chunk-GQVYQCY5.js +396 -0
  12. package/dist/{chunk-TXO34J3O.js → chunk-H7JW4L7H.js} +1 -1
  13. package/dist/{chunk-QFBKWDYR.js → chunk-IFGDPIFI.js} +3 -3
  14. package/dist/chunk-K6XHCUFL.js +123 -0
  15. package/dist/{chunk-PIJGYMQZ.js → chunk-KNDVXXKC.js} +1 -1
  16. package/dist/chunk-L6NB43WV.js +472 -0
  17. package/dist/{chunk-FEQ2CQ3Y.js → chunk-LB6P4CD5.js} +20 -7
  18. package/dist/chunk-MGDEINGP.js +99 -0
  19. package/dist/chunk-MQUJNOHK.js +58 -0
  20. package/dist/chunk-P5EPF6MB.js +182 -0
  21. package/dist/chunk-VR5NE7PZ.js +45 -0
  22. package/dist/chunk-WZI3OAE5.js +111 -0
  23. package/dist/chunk-Z2XBWN7A.js +247 -0
  24. package/dist/{chunk-O5V7SD5C.js → chunk-ZZA73MFY.js} +1 -1
  25. package/dist/commands/archive.d.ts +11 -0
  26. package/dist/commands/archive.js +11 -0
  27. package/dist/commands/context.d.ts +1 -1
  28. package/dist/commands/context.js +6 -4
  29. package/dist/commands/doctor.js +6 -6
  30. package/dist/commands/graph.js +2 -2
  31. package/dist/commands/link.js +1 -1
  32. package/dist/commands/migrate-observations.d.ts +19 -0
  33. package/dist/commands/migrate-observations.js +13 -0
  34. package/dist/commands/observe.js +5 -2
  35. package/dist/commands/rebuild.d.ts +11 -0
  36. package/dist/commands/rebuild.js +12 -0
  37. package/dist/commands/reflect.d.ts +11 -0
  38. package/dist/commands/reflect.js +13 -0
  39. package/dist/commands/replay.d.ts +16 -0
  40. package/dist/commands/replay.js +14 -0
  41. package/dist/commands/setup.js +2 -2
  42. package/dist/commands/sleep.d.ts +1 -0
  43. package/dist/commands/sleep.js +29 -6
  44. package/dist/commands/status.js +6 -6
  45. package/dist/commands/sync-bd.d.ts +10 -0
  46. package/dist/commands/sync-bd.js +9 -0
  47. package/dist/commands/wake.js +53 -35
  48. package/dist/{context-COo8oq1k.d.ts → context-BUGaWpyL.d.ts} +1 -0
  49. package/dist/index.d.ts +55 -20
  50. package/dist/index.js +67 -16
  51. package/hooks/clawvault/HOOK.md +3 -2
  52. package/hooks/clawvault/handler.js +51 -0
  53. package/hooks/clawvault/handler.test.js +20 -0
  54. package/package.json +2 -2
@@ -0,0 +1,396 @@
1
+ import {
2
+ archiveObservations
3
+ } from "./chunk-MQUJNOHK.js";
4
+ import {
5
+ normalizeObservationContent,
6
+ parseObservationMarkdown
7
+ } from "./chunk-K6XHCUFL.js";
8
+ import {
9
+ formatIsoWeekKey,
10
+ getIsoWeek,
11
+ getIsoWeekRange,
12
+ getReflectionsRoot,
13
+ listObservationFiles,
14
+ parseDateKey
15
+ } from "./chunk-Z2XBWN7A.js";
16
+
17
+ // src/observer/reflection-service.ts
18
+ import * as fs from "fs";
19
+ import * as path from "path";
20
+ var OPEN_LOOP_RE = /\b(open loop|todo|follow[- ]?up|blocked|pending|unresolved|still need)\b/i;
21
+ var CHANGE_RE = /\b(changed?|shift(?:ed)?|switched|moved|instead|no longer|pivot(?:ed)?)\b/i;
22
+ function normalizeDays(days) {
23
+ if (!Number.isFinite(days)) return 14;
24
+ return Math.max(1, Math.floor(days));
25
+ }
26
+ function shouldIncludeDate(date, fromDate, toDate) {
27
+ if (date < fromDate) return false;
28
+ if (date > toDate) return false;
29
+ return true;
30
+ }
31
+ function listReflectionFiles(vaultPath) {
32
+ const reflectionsRoot = getReflectionsRoot(vaultPath);
33
+ if (!fs.existsSync(reflectionsRoot)) {
34
+ return [];
35
+ }
36
+ return fs.readdirSync(reflectionsRoot, { withFileTypes: true }).filter((entry) => entry.isFile() && /^\d{4}-W\d{2}\.md$/.test(entry.name)).map((entry) => path.join(reflectionsRoot, entry.name)).sort((left, right) => left.localeCompare(right));
37
+ }
38
+ function extractPriorReflectionKeys(vaultPath, currentWeek) {
39
+ const keys = /* @__PURE__ */ new Set();
40
+ for (const filePath of listReflectionFiles(vaultPath)) {
41
+ const weekKey = path.basename(filePath, ".md");
42
+ if (weekKey >= currentWeek) {
43
+ continue;
44
+ }
45
+ const content = fs.readFileSync(filePath, "utf-8");
46
+ for (const line of content.split(/\r?\n/)) {
47
+ const match = line.match(/^- (.+)$/);
48
+ if (!match?.[1]) continue;
49
+ const value = match[1].trim();
50
+ if (value.startsWith("ledger/observations/")) continue;
51
+ keys.add(normalizeObservationContent(value));
52
+ }
53
+ }
54
+ return keys;
55
+ }
56
+ function mergeUnique(target, incoming) {
57
+ const seen = new Set(target.map((item) => normalizeObservationContent(item)));
58
+ const merged = [...target];
59
+ for (const item of incoming) {
60
+ const normalized = normalizeObservationContent(item);
61
+ if (!normalized || seen.has(normalized)) continue;
62
+ seen.add(normalized);
63
+ merged.push(item);
64
+ }
65
+ return merged;
66
+ }
67
+ function parseExistingReflectionSections(content) {
68
+ const sections = {
69
+ stablePatterns: [],
70
+ keyDecisions: [],
71
+ openLoops: [],
72
+ changes: [],
73
+ citations: []
74
+ };
75
+ let current = null;
76
+ for (const rawLine of content.split(/\r?\n/)) {
77
+ const line = rawLine.trim();
78
+ if (line === "## Stable Patterns") {
79
+ current = "stablePatterns";
80
+ continue;
81
+ }
82
+ if (line === "## Key Decisions") {
83
+ current = "keyDecisions";
84
+ continue;
85
+ }
86
+ if (line === "## Open Loops") {
87
+ current = "openLoops";
88
+ continue;
89
+ }
90
+ if (line === "## Changes") {
91
+ current = "changes";
92
+ continue;
93
+ }
94
+ if (line === "## Citations") {
95
+ current = "citations";
96
+ continue;
97
+ }
98
+ if (!current) continue;
99
+ const bullet = line.match(/^- (.+)$/);
100
+ if (!bullet?.[1]) continue;
101
+ sections[current].push(bullet[1].trim());
102
+ }
103
+ return sections;
104
+ }
105
+ function classifyItem(item) {
106
+ if (OPEN_LOOP_RE.test(item.content)) {
107
+ return "openLoops";
108
+ }
109
+ if (item.type === "decision" || item.type === "commitment" || item.type === "milestone") {
110
+ return "keyDecisions";
111
+ }
112
+ if (CHANGE_RE.test(item.content)) {
113
+ return "changes";
114
+ }
115
+ return "stablePatterns";
116
+ }
117
+ function toObservationCitationPath(date) {
118
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
119
+ return `ledger/observations/${date}.md`;
120
+ }
121
+ const [year, month, day] = date.split("-");
122
+ return `ledger/observations/${year}/${month}/${day}.md`;
123
+ }
124
+ function buildSectionDraft(promoted) {
125
+ const sections = {
126
+ stablePatterns: [],
127
+ keyDecisions: [],
128
+ openLoops: [],
129
+ changes: [],
130
+ citations: []
131
+ };
132
+ for (const item of promoted) {
133
+ sections[classifyItem(item)].push(
134
+ `[${item.type}|c=${item.confidence.toFixed(2)}|i=${item.importance.toFixed(2)}] ${item.content}`
135
+ );
136
+ for (const date of item.dates) {
137
+ sections.citations.push(toObservationCitationPath(date));
138
+ }
139
+ }
140
+ sections.citations = [...new Set(sections.citations)].sort((left, right) => left.localeCompare(right));
141
+ return sections;
142
+ }
143
+ function formatWeekTitle(weekKey) {
144
+ const [yearRaw, weekRaw] = weekKey.split("-W");
145
+ const year = Number.parseInt(yearRaw, 10);
146
+ const week = Number.parseInt(weekRaw, 10);
147
+ const range = getIsoWeekRange(year, week);
148
+ const monthFormatter = new Intl.DateTimeFormat("en-US", { month: "short", day: "numeric", timeZone: "UTC" });
149
+ return `# Week ${week}, ${year} (${monthFormatter.format(range.start)}-${monthFormatter.format(range.end)})`;
150
+ }
151
+ function renderReflectionMarkdown(weekKey, sections) {
152
+ const lines = [];
153
+ lines.push(formatWeekTitle(weekKey));
154
+ lines.push("");
155
+ lines.push("## Stable Patterns");
156
+ for (const item of sections.stablePatterns) lines.push(`- ${item}`);
157
+ lines.push("");
158
+ lines.push("## Key Decisions");
159
+ for (const item of sections.keyDecisions) lines.push(`- ${item}`);
160
+ lines.push("");
161
+ lines.push("## Open Loops");
162
+ for (const item of sections.openLoops) lines.push(`- ${item}`);
163
+ lines.push("");
164
+ lines.push("## Changes");
165
+ for (const item of sections.changes) lines.push(`- ${item}`);
166
+ lines.push("");
167
+ lines.push("## Citations");
168
+ for (const item of sections.citations) lines.push(`- ${item}`);
169
+ lines.push("");
170
+ return lines.join("\n").trim();
171
+ }
172
+ function promoteWeekRecords(records) {
173
+ const grouped = /* @__PURE__ */ new Map();
174
+ for (const record of records) {
175
+ const key = normalizeObservationContent(record.content);
176
+ const existing = grouped.get(key);
177
+ if (!existing) {
178
+ grouped.set(key, {
179
+ key,
180
+ type: record.type,
181
+ confidence: record.confidence,
182
+ importance: record.importance,
183
+ content: record.content,
184
+ dates: /* @__PURE__ */ new Set([record.date])
185
+ });
186
+ continue;
187
+ }
188
+ existing.dates.add(record.date);
189
+ if (record.importance > existing.importance) {
190
+ existing.importance = record.importance;
191
+ existing.type = record.type;
192
+ existing.content = record.content;
193
+ }
194
+ if (record.confidence > existing.confidence) {
195
+ existing.confidence = record.confidence;
196
+ }
197
+ grouped.set(key, existing);
198
+ }
199
+ const promoted = [];
200
+ for (const item of grouped.values()) {
201
+ if (item.importance >= 0.8) {
202
+ promoted.push(item);
203
+ continue;
204
+ }
205
+ if (item.importance >= 0.4 && item.dates.size >= 2) {
206
+ promoted.push(item);
207
+ }
208
+ }
209
+ return promoted;
210
+ }
211
+ function resolveProvider() {
212
+ if (process.env.CLAWVAULT_NO_LLM) return null;
213
+ if (process.env.ANTHROPIC_API_KEY) return "anthropic";
214
+ if (process.env.OPENAI_API_KEY) return "openai";
215
+ if (process.env.GEMINI_API_KEY) return "gemini";
216
+ return null;
217
+ }
218
+ async function callOpenAI(prompt, model) {
219
+ const apiKey = process.env.OPENAI_API_KEY;
220
+ if (!apiKey) return "";
221
+ const response = await fetch("https://api.openai.com/v1/chat/completions", {
222
+ method: "POST",
223
+ headers: {
224
+ "content-type": "application/json",
225
+ authorization: `Bearer ${apiKey}`
226
+ },
227
+ body: JSON.stringify({
228
+ model: model ?? "gpt-4o-mini",
229
+ temperature: 0.1,
230
+ messages: [{ role: "user", content: prompt }]
231
+ })
232
+ });
233
+ if (!response.ok) return "";
234
+ const payload = await response.json();
235
+ return payload.choices?.[0]?.message?.content?.trim() ?? "";
236
+ }
237
+ async function callAnthropic(prompt, model) {
238
+ const apiKey = process.env.ANTHROPIC_API_KEY;
239
+ if (!apiKey) return "";
240
+ const response = await fetch("https://api.anthropic.com/v1/messages", {
241
+ method: "POST",
242
+ headers: {
243
+ "content-type": "application/json",
244
+ "x-api-key": apiKey,
245
+ "anthropic-version": "2023-06-01"
246
+ },
247
+ body: JSON.stringify({
248
+ model: model ?? "claude-3-5-haiku-latest",
249
+ max_tokens: 1200,
250
+ temperature: 0.1,
251
+ messages: [{ role: "user", content: prompt }]
252
+ })
253
+ });
254
+ if (!response.ok) return "";
255
+ const payload = await response.json();
256
+ return payload.content?.filter((entry) => entry.type === "text" && entry.text).map((entry) => entry.text).join("\n").trim() ?? "";
257
+ }
258
+ async function callGemini(prompt, model) {
259
+ const apiKey = process.env.GEMINI_API_KEY;
260
+ if (!apiKey) return "";
261
+ const response = await fetch(
262
+ `https://generativelanguage.googleapis.com/v1beta/models/${model ?? "gemini-2.0-flash"}:generateContent?key=${apiKey}`,
263
+ {
264
+ method: "POST",
265
+ headers: { "content-type": "application/json" },
266
+ body: JSON.stringify({
267
+ contents: [{ parts: [{ text: prompt }] }],
268
+ generationConfig: { temperature: 0.1, maxOutputTokens: 1200 }
269
+ })
270
+ }
271
+ );
272
+ if (!response.ok) return "";
273
+ const payload = await response.json();
274
+ return payload.candidates?.[0]?.content?.parts?.[0]?.text?.trim() ?? "";
275
+ }
276
+ async function maybeGenerateLlmReflection(weekKey, sections) {
277
+ const provider = resolveProvider();
278
+ if (!provider) {
279
+ return null;
280
+ }
281
+ const prompt = [
282
+ "Rewrite the weekly reflection draft while preserving section structure and bullets.",
283
+ "Return markdown only using these exact headers:",
284
+ "# Week <N>, <YYYY> (...)",
285
+ "## Stable Patterns",
286
+ "## Key Decisions",
287
+ "## Open Loops",
288
+ "## Changes",
289
+ "## Citations",
290
+ "",
291
+ `Week key: ${weekKey}`,
292
+ "",
293
+ renderReflectionMarkdown(weekKey, sections)
294
+ ].join("\n");
295
+ try {
296
+ const output = provider === "anthropic" ? await callAnthropic(prompt) : provider === "gemini" ? await callGemini(prompt) : await callOpenAI(prompt);
297
+ if (!output.trim()) {
298
+ return null;
299
+ }
300
+ const cleaned = output.replace(/^```(?:markdown)?\s*/i, "").replace(/\s*```$/, "").trim();
301
+ if (cleaned.includes("## Stable Patterns") && cleaned.includes("## Key Decisions") && cleaned.includes("## Open Loops") && cleaned.includes("## Changes") && cleaned.includes("## Citations")) {
302
+ return cleaned;
303
+ }
304
+ return null;
305
+ } catch {
306
+ return null;
307
+ }
308
+ }
309
+ async function runReflection(options) {
310
+ const days = normalizeDays(options.days);
311
+ const dryRun = options.dryRun ?? false;
312
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
313
+ const nowDate = now();
314
+ const toDate = nowDate.toISOString().slice(0, 10);
315
+ const fromDateDate = new Date(nowDate);
316
+ fromDateDate.setDate(nowDate.getDate() - (days - 1));
317
+ const fromDate = fromDateDate.toISOString().slice(0, 10);
318
+ const observationFiles = listObservationFiles(options.vaultPath, {
319
+ includeLegacy: true,
320
+ includeArchive: false,
321
+ dedupeByDate: true
322
+ }).filter((entry) => shouldIncludeDate(entry.date, fromDate, toDate));
323
+ const recordsByWeek = /* @__PURE__ */ new Map();
324
+ for (const entry of observationFiles) {
325
+ const parsedDate = parseDateKey(entry.date);
326
+ if (!parsedDate) continue;
327
+ const week = getIsoWeek(parsedDate);
328
+ const weekKey = formatIsoWeekKey(week);
329
+ const markdown = fs.readFileSync(entry.path, "utf-8");
330
+ const parsedRecords = parseObservationMarkdown(markdown);
331
+ const bucket = recordsByWeek.get(weekKey) ?? [];
332
+ for (const record of parsedRecords) {
333
+ bucket.push({
334
+ date: record.date,
335
+ type: record.type,
336
+ confidence: record.confidence,
337
+ importance: record.importance,
338
+ content: record.content
339
+ });
340
+ }
341
+ recordsByWeek.set(weekKey, bucket);
342
+ }
343
+ const processedWeeks = [...recordsByWeek.keys()].sort((left, right) => left.localeCompare(right));
344
+ const writtenFiles = [];
345
+ for (const weekKey of processedWeeks) {
346
+ const promoted = promoteWeekRecords(recordsByWeek.get(weekKey) ?? []);
347
+ const priorKeys = extractPriorReflectionKeys(options.vaultPath, weekKey);
348
+ const unseenPromoted = promoted.filter((item) => !priorKeys.has(item.key));
349
+ if (unseenPromoted.length === 0) {
350
+ continue;
351
+ }
352
+ const reflectionPath = path.join(getReflectionsRoot(options.vaultPath), `${weekKey}.md`);
353
+ const existing = fs.existsSync(reflectionPath) ? fs.readFileSync(reflectionPath, "utf-8") : "";
354
+ const existingSections = existing ? parseExistingReflectionSections(existing) : {
355
+ stablePatterns: [],
356
+ keyDecisions: [],
357
+ openLoops: [],
358
+ changes: [],
359
+ citations: []
360
+ };
361
+ const draftSections = buildSectionDraft(unseenPromoted);
362
+ const mergedSections = {
363
+ stablePatterns: mergeUnique(existingSections.stablePatterns, draftSections.stablePatterns),
364
+ keyDecisions: mergeUnique(existingSections.keyDecisions, draftSections.keyDecisions),
365
+ openLoops: mergeUnique(existingSections.openLoops, draftSections.openLoops),
366
+ changes: mergeUnique(existingSections.changes, draftSections.changes),
367
+ citations: mergeUnique(existingSections.citations, draftSections.citations)
368
+ };
369
+ const llmMarkdown = await maybeGenerateLlmReflection(weekKey, mergedSections);
370
+ const markdown = llmMarkdown ?? renderReflectionMarkdown(weekKey, mergedSections);
371
+ if (dryRun) {
372
+ writtenFiles.push(reflectionPath);
373
+ continue;
374
+ }
375
+ fs.mkdirSync(path.dirname(reflectionPath), { recursive: true });
376
+ fs.writeFileSync(reflectionPath, `${markdown.trim()}
377
+ `, "utf-8");
378
+ writtenFiles.push(reflectionPath);
379
+ }
380
+ const archive = dryRun ? null : archiveObservations(options.vaultPath, {
381
+ olderThanDays: 14,
382
+ dryRun: false,
383
+ now
384
+ });
385
+ return {
386
+ processedWeeks: processedWeeks.length,
387
+ writtenWeeks: writtenFiles.length,
388
+ dryRun,
389
+ files: writtenFiles,
390
+ archive
391
+ };
392
+ }
393
+
394
+ export {
395
+ runReflection
396
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  buildOrUpdateMemoryGraphIndex,
3
3
  loadMemoryGraphIndex
4
- } from "./chunk-O5V7SD5C.js";
4
+ } from "./chunk-ZZA73MFY.js";
5
5
  import {
6
6
  resolveVaultPath
7
7
  } from "./chunk-MXSSG3QU.js";
@@ -8,10 +8,10 @@ import {
8
8
  hasQmd,
9
9
  qmdEmbed,
10
10
  qmdUpdate
11
- } from "./chunk-MIIXBNO3.js";
11
+ } from "./chunk-DHBDH4DN.js";
12
12
  import {
13
13
  buildOrUpdateMemoryGraphIndex
14
- } from "./chunk-O5V7SD5C.js";
14
+ } from "./chunk-ZZA73MFY.js";
15
15
 
16
16
  // src/lib/vault.ts
17
17
  import * as fs from "fs";
@@ -111,7 +111,7 @@ var ClawVault = class {
111
111
  this.search.clear();
112
112
  const files = await glob("**/*.md", {
113
113
  cwd: this.config.path,
114
- ignore: ["**/node_modules/**", "**/.*"]
114
+ ignore: ["**/node_modules/**", "**/.*", "**/ledger/archive/**"]
115
115
  });
116
116
  for (const file of files) {
117
117
  const doc = await this.loadDocument(file);
@@ -0,0 +1,123 @@
1
+ // src/lib/observation-format.ts
2
+ var DATE_HEADING_RE = /^##\s+(\d{4}-\d{2}-\d{2})\s*$/;
3
+ var SCORED_LINE_RE = /^(?:-\s*)?\[(decision|preference|fact|commitment|milestone|lesson|relationship|project)\|c=(0(?:\.\d+)?|1(?:\.0+)?)\|i=(0(?:\.\d+)?|1(?:\.0+)?)\]\s+(.+)$/i;
4
+ var EMOJI_LINE_RE = /^(?:-\s*)?(🔴|🟡|🟢)\s+(\d{2}:\d{2})?\s*(.+)$/u;
5
+ var DECISION_RE = /\b(decis(?:ion|ions)?|decid(?:e|ed|ing)|chose|selected|opted|went with|picked)\b/i;
6
+ var PREFERENCE_RE = /\b(prefer(?:ence|s|red)?|likes?|dislikes?|default to|always use|never use)\b/i;
7
+ var COMMITMENT_RE = /\b(commit(?:ment|ted)?|promised|deadline|due|scheduled|will deliver|agreed to)\b/i;
8
+ var MILESTONE_RE = /\b(released?|shipped|launched|merged|published|milestone|v\d+\.\d+)\b/i;
9
+ var LESSON_RE = /\b(learn(?:ed|ing|t)|lesson|insight|realized|discovered|never again)\b/i;
10
+ var RELATIONSHIP_RE = /\b(talked to|met with|spoke with|asked|client|partner|teammate|colleague)\b/i;
11
+ var PROJECT_RE = /\b(project|feature|service|repo|api|roadmap|sprint)\b/i;
12
+ function clamp01(value) {
13
+ if (!Number.isFinite(value)) return 0;
14
+ if (value < 0) return 0;
15
+ if (value > 1) return 1;
16
+ return value;
17
+ }
18
+ function scoreFromLegacyPriority(priority) {
19
+ if (priority === "\u{1F534}") return 0.9;
20
+ if (priority === "\u{1F7E1}") return 0.6;
21
+ return 0.2;
22
+ }
23
+ function confidenceFromLegacyPriority(priority) {
24
+ if (priority === "\u{1F534}") return 0.9;
25
+ if (priority === "\u{1F7E1}") return 0.8;
26
+ return 0.7;
27
+ }
28
+ function inferObservationType(content) {
29
+ if (DECISION_RE.test(content)) return "decision";
30
+ if (COMMITMENT_RE.test(content)) return "commitment";
31
+ if (MILESTONE_RE.test(content)) return "milestone";
32
+ if (PREFERENCE_RE.test(content)) return "preference";
33
+ if (LESSON_RE.test(content)) return "lesson";
34
+ if (RELATIONSHIP_RE.test(content)) return "relationship";
35
+ if (PROJECT_RE.test(content)) return "project";
36
+ return "fact";
37
+ }
38
+ function formatScore(value) {
39
+ return clamp01(value).toFixed(2);
40
+ }
41
+ function normalizeObservationContent(content) {
42
+ return content.replace(/^\d{2}:\d{2}\s+/, "").replace(/\s+/g, " ").trim().toLowerCase();
43
+ }
44
+ function parseObservationLine(line, date) {
45
+ const scored = line.match(SCORED_LINE_RE);
46
+ if (scored) {
47
+ return {
48
+ date,
49
+ type: scored[1].toLowerCase(),
50
+ confidence: clamp01(Number.parseFloat(scored[2])),
51
+ importance: clamp01(Number.parseFloat(scored[3])),
52
+ content: scored[4].trim(),
53
+ format: "scored",
54
+ rawLine: line
55
+ };
56
+ }
57
+ const emoji = line.match(EMOJI_LINE_RE);
58
+ if (!emoji) {
59
+ return null;
60
+ }
61
+ const priority = emoji[1];
62
+ const time = emoji[2]?.trim();
63
+ const text = emoji[3].trim();
64
+ const content = time ? `${time} ${text}` : text;
65
+ return {
66
+ date,
67
+ type: inferObservationType(content),
68
+ confidence: confidenceFromLegacyPriority(priority),
69
+ importance: scoreFromLegacyPriority(priority),
70
+ content,
71
+ format: "emoji",
72
+ priority,
73
+ time,
74
+ rawLine: line
75
+ };
76
+ }
77
+ function parseObservationMarkdown(markdown) {
78
+ const parsed = [];
79
+ let currentDate = "";
80
+ for (const line of markdown.split(/\r?\n/)) {
81
+ const heading = line.match(DATE_HEADING_RE);
82
+ if (heading) {
83
+ currentDate = heading[1];
84
+ continue;
85
+ }
86
+ if (!currentDate) {
87
+ continue;
88
+ }
89
+ const record = parseObservationLine(line.trim(), currentDate);
90
+ if (record) {
91
+ parsed.push(record);
92
+ }
93
+ }
94
+ return parsed;
95
+ }
96
+ function renderScoredObservationLine(record) {
97
+ return `- [${record.type}|c=${formatScore(record.confidence)}|i=${formatScore(record.importance)}] ${record.content.trim()}`;
98
+ }
99
+ function renderObservationMarkdown(sections) {
100
+ const chunks = [];
101
+ const dates = [...sections.keys()].sort((left, right) => left.localeCompare(right));
102
+ for (const date of dates) {
103
+ const lines = sections.get(date) ?? [];
104
+ if (lines.length === 0) continue;
105
+ chunks.push(`## ${date}`);
106
+ chunks.push("");
107
+ for (const line of lines) {
108
+ chunks.push(renderScoredObservationLine(line));
109
+ }
110
+ chunks.push("");
111
+ }
112
+ return chunks.join("\n").trim();
113
+ }
114
+
115
+ export {
116
+ DATE_HEADING_RE,
117
+ inferObservationType,
118
+ normalizeObservationContent,
119
+ parseObservationLine,
120
+ parseObservationMarkdown,
121
+ renderScoredObservationLine,
122
+ renderObservationMarkdown
123
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  DEFAULT_CATEGORIES,
3
3
  hasQmd
4
- } from "./chunk-MIIXBNO3.js";
4
+ } from "./chunk-DHBDH4DN.js";
5
5
 
6
6
  // src/commands/setup.ts
7
7
  import * as fs from "fs";