clawvault 1.9.4 → 1.9.6
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/dist/{chunk-DII3KLBF.js → chunk-2MP4EHJ7.js} +61 -7
- package/dist/{chunk-I3UNVUHJ.js → chunk-SIDM2I2C.js} +20 -8
- package/dist/{chunk-AVPHNEDB.js → chunk-UBRYOIII.js} +122 -24
- package/dist/commands/context.js +1 -1
- package/dist/commands/observe.js +2 -2
- package/dist/commands/sleep.js +1 -1
- package/dist/commands/wake.js +35 -2
- package/dist/index.d.ts +9 -1
- package/dist/index.js +3 -3
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Observer,
|
|
3
3
|
parseSessionFile
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SIDM2I2C.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/observe.ts
|
|
7
7
|
import * as fs2 from "fs";
|
|
@@ -12,21 +12,25 @@ import { spawn } from "child_process";
|
|
|
12
12
|
import * as fs from "fs";
|
|
13
13
|
import * as path from "path";
|
|
14
14
|
import chokidar from "chokidar";
|
|
15
|
+
var DEFAULT_FLUSH_THRESHOLD_CHARS = 500;
|
|
15
16
|
var SessionWatcher = class {
|
|
16
17
|
watchPath;
|
|
17
18
|
observer;
|
|
18
19
|
ignoreInitial;
|
|
19
20
|
debounceMs;
|
|
21
|
+
flushThresholdChars;
|
|
20
22
|
watcher = null;
|
|
21
23
|
fileOffsets = /* @__PURE__ */ new Map();
|
|
22
24
|
pendingPaths = /* @__PURE__ */ new Set();
|
|
23
25
|
debounceTimer = null;
|
|
24
26
|
processingQueue = Promise.resolve();
|
|
27
|
+
bufferedChars = 0;
|
|
25
28
|
constructor(watchPath, observer, options = {}) {
|
|
26
29
|
this.watchPath = path.resolve(watchPath);
|
|
27
30
|
this.observer = observer;
|
|
28
31
|
this.ignoreInitial = options.ignoreInitial ?? false;
|
|
29
32
|
this.debounceMs = options.debounceMs ?? 500;
|
|
33
|
+
this.flushThresholdChars = Math.max(1, options.flushThresholdChars ?? DEFAULT_FLUSH_THRESHOLD_CHARS);
|
|
30
34
|
}
|
|
31
35
|
async start() {
|
|
32
36
|
if (!fs.existsSync(this.watchPath)) {
|
|
@@ -55,14 +59,22 @@ var SessionWatcher = class {
|
|
|
55
59
|
this.watcher?.once("ready", () => resolve3());
|
|
56
60
|
this.watcher?.once("error", (error) => reject(error));
|
|
57
61
|
});
|
|
62
|
+
if (this.ignoreInitial) {
|
|
63
|
+
this.primeInitialOffsets();
|
|
64
|
+
}
|
|
58
65
|
}
|
|
59
66
|
async stop() {
|
|
60
67
|
if (this.debounceTimer) {
|
|
61
68
|
clearTimeout(this.debounceTimer);
|
|
62
69
|
this.debounceTimer = null;
|
|
70
|
+
this.drainPendingPaths();
|
|
63
71
|
}
|
|
64
|
-
this.pendingPaths.clear();
|
|
65
72
|
await this.processingQueue.catch(() => void 0);
|
|
73
|
+
if (this.bufferedChars > 0) {
|
|
74
|
+
await this.observer.flush();
|
|
75
|
+
this.bufferedChars = 0;
|
|
76
|
+
}
|
|
77
|
+
this.pendingPaths.clear();
|
|
66
78
|
await this.watcher?.close();
|
|
67
79
|
this.watcher = null;
|
|
68
80
|
}
|
|
@@ -72,13 +84,16 @@ var SessionWatcher = class {
|
|
|
72
84
|
}
|
|
73
85
|
this.debounceTimer = setTimeout(() => {
|
|
74
86
|
this.debounceTimer = null;
|
|
75
|
-
|
|
76
|
-
this.pendingPaths.clear();
|
|
77
|
-
for (const changedPath of nextPaths) {
|
|
78
|
-
this.processingQueue = this.processingQueue.then(() => this.consumeFile(changedPath)).catch(() => void 0);
|
|
79
|
-
}
|
|
87
|
+
this.drainPendingPaths();
|
|
80
88
|
}, this.debounceMs);
|
|
81
89
|
}
|
|
90
|
+
drainPendingPaths() {
|
|
91
|
+
const nextPaths = [...this.pendingPaths];
|
|
92
|
+
this.pendingPaths.clear();
|
|
93
|
+
for (const changedPath of nextPaths) {
|
|
94
|
+
this.processingQueue = this.processingQueue.then(() => this.consumeFile(changedPath)).catch(() => void 0);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
82
97
|
async consumeFile(filePath) {
|
|
83
98
|
const resolved = path.resolve(filePath);
|
|
84
99
|
if (!fs.existsSync(resolved)) {
|
|
@@ -109,6 +124,45 @@ var SessionWatcher = class {
|
|
|
109
124
|
return;
|
|
110
125
|
}
|
|
111
126
|
await this.observer.processMessages(messages);
|
|
127
|
+
this.bufferedChars += chunk.length;
|
|
128
|
+
if (this.bufferedChars >= this.flushThresholdChars) {
|
|
129
|
+
await this.observer.flush();
|
|
130
|
+
this.bufferedChars = 0;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
primeInitialOffsets() {
|
|
134
|
+
for (const filePath of this.collectFiles(this.watchPath)) {
|
|
135
|
+
try {
|
|
136
|
+
const stats = fs.statSync(filePath);
|
|
137
|
+
if (stats.isFile()) {
|
|
138
|
+
this.fileOffsets.set(filePath, stats.size);
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
collectFiles(targetPath) {
|
|
145
|
+
if (!fs.existsSync(targetPath)) {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
const resolved = path.resolve(targetPath);
|
|
149
|
+
const stats = fs.statSync(resolved);
|
|
150
|
+
if (stats.isFile()) {
|
|
151
|
+
return [resolved];
|
|
152
|
+
}
|
|
153
|
+
if (!stats.isDirectory()) {
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
const collected = [];
|
|
157
|
+
for (const entry of fs.readdirSync(resolved, { withFileTypes: true })) {
|
|
158
|
+
const childPath = path.join(resolved, entry.name);
|
|
159
|
+
if (entry.isDirectory()) {
|
|
160
|
+
collected.push(...this.collectFiles(childPath));
|
|
161
|
+
} else if (entry.isFile()) {
|
|
162
|
+
collected.push(path.resolve(childPath));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return collected;
|
|
112
166
|
}
|
|
113
167
|
};
|
|
114
168
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// src/observer/compressor.ts
|
|
2
2
|
var DATE_HEADING_RE = /^##\s+(\d{4}-\d{2}-\d{2})\s*$/;
|
|
3
3
|
var OBSERVATION_LINE_RE = /^(🔴|🟡|🟢)\s+(.+)$/u;
|
|
4
|
-
var CRITICAL_RE = /(?:\b(?:decision|decided|chose|selected)\s
|
|
5
|
-
var
|
|
4
|
+
var CRITICAL_RE = /(?:\b(?:decision|decided|chose|chosen|selected|picked|opted|switched to)\s*:?|\bdecid(?:e|ed|ing|ion)\b|\berror\b|\bfail(?:ed|ure|ing)?\b|\bblock(?:ed|er)?\b|\bbreaking(?:\s+change)?s?\b|\bcritical\b|\b\w+\s+chosen\s+(?:for|over|as)\b)/i;
|
|
5
|
+
var DEADLINE_WITH_DATE_RE = /(?:(?:\bdeadline\b|\bdue(?:\s+date)?\b|\bcutoff\b).*(?:\d{4}-\d{2}-\d{2}|\d{1,2}\/\d{1,2}(?:\/\d{2,4})?|(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\s+\d{1,2})|(?:\d{4}-\d{2}-\d{2}|\d{1,2}\/\d{1,2}(?:\/\d{2,4})?|(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\s+\d{1,2}).*(?:\bdeadline\b|\bdue(?:\s+date)?\b|\bcutoff\b))/i;
|
|
6
|
+
var NOTABLE_RE = /\b(prefer(?:ence|s)?|likes?|dislikes?|context|pattern|architecture|approach|trade[- ]?off|milestone|stakeholder|teammate|collaborat(?:e|ed|ion)|discussion|notable|deadline|due|timeline)\b/i;
|
|
6
7
|
var Compressor = class {
|
|
7
8
|
model;
|
|
8
9
|
now;
|
|
@@ -53,7 +54,12 @@ var Compressor = class {
|
|
|
53
54
|
"- Group observations by date heading: ## YYYY-MM-DD",
|
|
54
55
|
"- Each line must follow: <emoji> <HH:MM> <observation>",
|
|
55
56
|
"- Priority emojis: \u{1F534} critical, \u{1F7E1} notable, \u{1F7E2} info",
|
|
56
|
-
"-
|
|
57
|
+
"- \u{1F534} for: decisions between alternatives, errors/failures, blockers, deadlines with explicit dates, breaking changes",
|
|
58
|
+
"- \u{1F7E1} for: preferences, architecture discussions, trade-offs, milestones, people interactions, notable context, routine deadlines",
|
|
59
|
+
"- \u{1F7E2} for: routine updates, deployments, builds, general info",
|
|
60
|
+
"- Preferences are \u{1F7E1} unless they indicate a breaking decision between alternatives.",
|
|
61
|
+
"- Each distinct error type or failure must be its own observation line; do not merge different errors.",
|
|
62
|
+
"- If multiple different errors occurred, list each separately with its specific error message.",
|
|
57
63
|
"- Keep observations concise and factual.",
|
|
58
64
|
"- Avoid duplicates when possible.",
|
|
59
65
|
"",
|
|
@@ -158,7 +164,7 @@ ${cleaned}`;
|
|
|
158
164
|
}
|
|
159
165
|
/**
|
|
160
166
|
* Post-process LLM output to enforce priority rules.
|
|
161
|
-
* Lines matching
|
|
167
|
+
* Lines matching critical rules get upgraded to 🔴, notable rules to 🟡.
|
|
162
168
|
*/
|
|
163
169
|
enforcePriorityRules(markdown) {
|
|
164
170
|
return markdown.split(/\r?\n/).map((line) => {
|
|
@@ -166,10 +172,10 @@ ${cleaned}`;
|
|
|
166
172
|
if (!match) return line;
|
|
167
173
|
const currentPriority = match[1];
|
|
168
174
|
const content = match[2];
|
|
169
|
-
if (
|
|
175
|
+
if (this.isCriticalContent(content) && currentPriority !== "\u{1F534}") {
|
|
170
176
|
return line.replace(/^🟡|^🟢/u, "\u{1F534}");
|
|
171
177
|
}
|
|
172
|
-
if (
|
|
178
|
+
if (this.isNotableContent(content) && currentPriority === "\u{1F7E2}") {
|
|
173
179
|
return line.replace(/^🟢/u, "\u{1F7E1}");
|
|
174
180
|
}
|
|
175
181
|
return line;
|
|
@@ -281,10 +287,16 @@ ${cleaned}`;
|
|
|
281
287
|
return chunks.join("\n").trim();
|
|
282
288
|
}
|
|
283
289
|
inferPriority(text) {
|
|
284
|
-
if (
|
|
285
|
-
if (
|
|
290
|
+
if (this.isCriticalContent(text)) return "\u{1F534}";
|
|
291
|
+
if (this.isNotableContent(text)) return "\u{1F7E1}";
|
|
286
292
|
return "\u{1F7E2}";
|
|
287
293
|
}
|
|
294
|
+
isCriticalContent(text) {
|
|
295
|
+
return CRITICAL_RE.test(text) || DEADLINE_WITH_DATE_RE.test(text);
|
|
296
|
+
}
|
|
297
|
+
isNotableContent(text) {
|
|
298
|
+
return NOTABLE_RE.test(text);
|
|
299
|
+
}
|
|
288
300
|
normalizeText(text) {
|
|
289
301
|
return text.replace(/\s+/g, " ").replace(/\[[^\]]+\]/g, "").trim().slice(0, 280);
|
|
290
302
|
}
|
|
@@ -58,11 +58,71 @@ function estimateTokens(text) {
|
|
|
58
58
|
}
|
|
59
59
|
return Math.ceil(text.length / 4);
|
|
60
60
|
}
|
|
61
|
+
function fitWithinBudget(items, budget) {
|
|
62
|
+
if (!Number.isFinite(budget) || budget <= 0) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
const sorted = items.map((item, index) => ({ ...item, index })).sort((a, b) => {
|
|
66
|
+
if (a.priority !== b.priority) {
|
|
67
|
+
return a.priority - b.priority;
|
|
68
|
+
}
|
|
69
|
+
return a.index - b.index;
|
|
70
|
+
});
|
|
71
|
+
let remaining = Math.floor(budget);
|
|
72
|
+
const fitted = [];
|
|
73
|
+
for (const item of sorted) {
|
|
74
|
+
if (!item.text.trim()) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const cost = estimateTokens(item.text);
|
|
78
|
+
if (cost <= remaining) {
|
|
79
|
+
fitted.push({ text: item.text, source: item.source });
|
|
80
|
+
remaining -= cost;
|
|
81
|
+
}
|
|
82
|
+
if (remaining <= 0) {
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return fitted;
|
|
87
|
+
}
|
|
61
88
|
|
|
62
89
|
// src/commands/context.ts
|
|
63
90
|
var DEFAULT_LIMIT = 5;
|
|
64
91
|
var MAX_SNIPPET_LENGTH = 320;
|
|
65
92
|
var OBSERVATION_LOOKBACK_DAYS = 7;
|
|
93
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
94
|
+
"a",
|
|
95
|
+
"an",
|
|
96
|
+
"and",
|
|
97
|
+
"are",
|
|
98
|
+
"as",
|
|
99
|
+
"at",
|
|
100
|
+
"be",
|
|
101
|
+
"by",
|
|
102
|
+
"for",
|
|
103
|
+
"from",
|
|
104
|
+
"how",
|
|
105
|
+
"in",
|
|
106
|
+
"is",
|
|
107
|
+
"it",
|
|
108
|
+
"of",
|
|
109
|
+
"on",
|
|
110
|
+
"or",
|
|
111
|
+
"that",
|
|
112
|
+
"the",
|
|
113
|
+
"this",
|
|
114
|
+
"to",
|
|
115
|
+
"was",
|
|
116
|
+
"were",
|
|
117
|
+
"what",
|
|
118
|
+
"when",
|
|
119
|
+
"where",
|
|
120
|
+
"who",
|
|
121
|
+
"why",
|
|
122
|
+
"with",
|
|
123
|
+
"you",
|
|
124
|
+
"your"
|
|
125
|
+
]);
|
|
66
126
|
function formatRelativeAge(date, now = Date.now()) {
|
|
67
127
|
const ageMs = Math.max(0, now - date.getTime());
|
|
68
128
|
const days = Math.floor(ageMs / (24 * 60 * 60 * 1e3));
|
|
@@ -97,6 +157,35 @@ function formatContextMarkdown(task, entries) {
|
|
|
97
157
|
}
|
|
98
158
|
return output.trimEnd();
|
|
99
159
|
}
|
|
160
|
+
function extractKeywords(text) {
|
|
161
|
+
const raw = text.toLowerCase().match(/[a-z0-9]+/g) ?? [];
|
|
162
|
+
const seen = /* @__PURE__ */ new Set();
|
|
163
|
+
const keywords = [];
|
|
164
|
+
for (const token of raw) {
|
|
165
|
+
if (token.length < 2 || STOP_WORDS.has(token) || seen.has(token)) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
seen.add(token);
|
|
169
|
+
keywords.push(token);
|
|
170
|
+
}
|
|
171
|
+
return keywords;
|
|
172
|
+
}
|
|
173
|
+
function computeKeywordOverlapScore(queryKeywords, text) {
|
|
174
|
+
if (queryKeywords.length === 0) {
|
|
175
|
+
return 1;
|
|
176
|
+
}
|
|
177
|
+
const haystack = new Set(extractKeywords(text));
|
|
178
|
+
let matches = 0;
|
|
179
|
+
for (const keyword of queryKeywords) {
|
|
180
|
+
if (haystack.has(keyword)) {
|
|
181
|
+
matches += 1;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (matches === 0) {
|
|
185
|
+
return 0.1;
|
|
186
|
+
}
|
|
187
|
+
return matches / queryKeywords.length;
|
|
188
|
+
}
|
|
100
189
|
function estimateSnippet(source) {
|
|
101
190
|
const normalized = source.replace(/\s+/g, " ").trim();
|
|
102
191
|
if (!normalized) {
|
|
@@ -134,11 +223,6 @@ function observationPriorityToRank(priority) {
|
|
|
134
223
|
if (priority === "\u{1F7E1}") return 4;
|
|
135
224
|
return 5;
|
|
136
225
|
}
|
|
137
|
-
function observationPriorityScore(priority) {
|
|
138
|
-
if (priority === "\u{1F534}") return 1;
|
|
139
|
-
if (priority === "\u{1F7E1}") return 0.7;
|
|
140
|
-
return 0.4;
|
|
141
|
-
}
|
|
142
226
|
function isLikelyDailyNote(document) {
|
|
143
227
|
const normalizedPath = document.path.split(path2.sep).join("/").toLowerCase();
|
|
144
228
|
if (normalizedPath.includes("/daily/")) {
|
|
@@ -212,7 +296,7 @@ async function buildDailyContextItems(vault) {
|
|
|
212
296
|
}
|
|
213
297
|
return items;
|
|
214
298
|
}
|
|
215
|
-
function buildObservationContextItems(vaultPath) {
|
|
299
|
+
function buildObservationContextItems(vaultPath, queryKeywords) {
|
|
216
300
|
const observationMarkdown = readObservations(vaultPath, OBSERVATION_LOOKBACK_DAYS);
|
|
217
301
|
const parsed = parseObservationLines(observationMarkdown);
|
|
218
302
|
const items = [];
|
|
@@ -227,7 +311,7 @@ function buildObservationContextItems(vaultPath) {
|
|
|
227
311
|
title: `${observation.priority} observation (${date})`,
|
|
228
312
|
path: `observations/${date}.md`,
|
|
229
313
|
category: "observations",
|
|
230
|
-
score:
|
|
314
|
+
score: computeKeywordOverlapScore(queryKeywords, observation.content),
|
|
231
315
|
snippet,
|
|
232
316
|
modified: modifiedDate.toISOString(),
|
|
233
317
|
age: formatRelativeAge(modifiedDate),
|
|
@@ -279,21 +363,31 @@ function applyTokenBudget(items, task, budget) {
|
|
|
279
363
|
return { context: fullContext, markdown: fullMarkdown };
|
|
280
364
|
}
|
|
281
365
|
const normalizedBudget = Math.max(1, Math.floor(budget));
|
|
366
|
+
if (estimateTokens(fullMarkdown) <= normalizedBudget) {
|
|
367
|
+
return { context: fullContext, markdown: fullMarkdown };
|
|
368
|
+
}
|
|
282
369
|
const header = `## Relevant Context for: ${task}
|
|
283
370
|
|
|
284
371
|
`;
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
const cost = estimateTokens(renderEntryBlock(item.entry));
|
|
292
|
-
if (cost <= remaining) {
|
|
293
|
-
selectedEntries.push(item.entry);
|
|
294
|
-
remaining -= cost;
|
|
295
|
-
}
|
|
372
|
+
const headerCost = estimateTokens(header);
|
|
373
|
+
if (headerCost >= normalizedBudget) {
|
|
374
|
+
return {
|
|
375
|
+
context: [],
|
|
376
|
+
markdown: truncateToBudget(header.trimEnd(), normalizedBudget)
|
|
377
|
+
};
|
|
296
378
|
}
|
|
379
|
+
const fitted = fitWithinBudget(
|
|
380
|
+
items.map((item, index) => ({
|
|
381
|
+
text: renderEntryBlock(item.entry),
|
|
382
|
+
priority: item.priority,
|
|
383
|
+
source: String(index)
|
|
384
|
+
})),
|
|
385
|
+
normalizedBudget - headerCost
|
|
386
|
+
);
|
|
387
|
+
const selectedEntries = fitted.map((item) => {
|
|
388
|
+
const index = Number.parseInt(item.source, 10);
|
|
389
|
+
return Number.isNaN(index) ? null : items[index]?.entry ?? null;
|
|
390
|
+
}).filter((entry) => Boolean(entry));
|
|
297
391
|
const markdown = truncateToBudget(formatContextMarkdown(task, selectedEntries), normalizedBudget);
|
|
298
392
|
return {
|
|
299
393
|
context: selectedEntries,
|
|
@@ -310,20 +404,24 @@ async function buildContext(task, options) {
|
|
|
310
404
|
const limit = Math.max(1, options.limit ?? DEFAULT_LIMIT);
|
|
311
405
|
const recent = options.recent ?? true;
|
|
312
406
|
const includeObservations = options.includeObservations ?? true;
|
|
407
|
+
const queryKeywords = extractKeywords(normalizedTask);
|
|
313
408
|
const searchResults = await vault.vsearch(normalizedTask, {
|
|
314
409
|
limit,
|
|
315
410
|
temporalBoost: recent
|
|
316
411
|
});
|
|
317
412
|
const searchItems = buildSearchContextItems(vault, searchResults);
|
|
318
413
|
const dailyItems = await buildDailyContextItems(vault);
|
|
319
|
-
const observationItems = includeObservations ? buildObservationContextItems(vault.getPath()) : [];
|
|
320
|
-
const
|
|
321
|
-
const
|
|
322
|
-
const
|
|
414
|
+
const observationItems = includeObservations ? buildObservationContextItems(vault.getPath(), queryKeywords) : [];
|
|
415
|
+
const byScoreDesc = (left, right) => right.entry.score - left.entry.score;
|
|
416
|
+
const redObservations = observationItems.filter((item) => item.priority === 1).sort(byScoreDesc);
|
|
417
|
+
const yellowObservations = observationItems.filter((item) => item.priority === 4).sort(byScoreDesc);
|
|
418
|
+
const greenObservations = observationItems.filter((item) => item.priority === 5).sort(byScoreDesc);
|
|
419
|
+
const sortedDailyItems = [...dailyItems].sort(byScoreDesc);
|
|
420
|
+
const sortedSearchItems = [...searchItems].sort(byScoreDesc);
|
|
323
421
|
const ordered = [
|
|
324
422
|
...redObservations,
|
|
325
|
-
...
|
|
326
|
-
...
|
|
423
|
+
...sortedDailyItems,
|
|
424
|
+
...sortedSearchItems,
|
|
327
425
|
...yellowObservations,
|
|
328
426
|
...greenObservations
|
|
329
427
|
];
|
package/dist/commands/context.js
CHANGED
package/dist/commands/observe.js
CHANGED
package/dist/commands/sleep.js
CHANGED
package/dist/commands/wake.js
CHANGED
|
@@ -15,6 +15,9 @@ import * as fs from "fs";
|
|
|
15
15
|
import * as path from "path";
|
|
16
16
|
var DEFAULT_HANDOFF_LIMIT = 3;
|
|
17
17
|
var OBSERVATION_HIGHLIGHT_RE = /^(🔴|🟡)\s+(.+)$/u;
|
|
18
|
+
var MAX_WAKE_RED_OBSERVATIONS = 20;
|
|
19
|
+
var MAX_WAKE_YELLOW_OBSERVATIONS = 10;
|
|
20
|
+
var MAX_WAKE_OUTPUT_LINES = 100;
|
|
18
21
|
function formatSummaryItems(items, maxItems = 2) {
|
|
19
22
|
const cleaned = items.map((item) => item.trim()).filter(Boolean);
|
|
20
23
|
if (cleaned.length === 0) return "";
|
|
@@ -60,23 +63,53 @@ function readRecentObservationHighlights(vaultPath) {
|
|
|
60
63
|
}
|
|
61
64
|
return highlights;
|
|
62
65
|
}
|
|
66
|
+
function timeFromObservationText(text) {
|
|
67
|
+
const match = text.match(/^([01]\d|2[0-3]):([0-5]\d)\b/);
|
|
68
|
+
if (!match) {
|
|
69
|
+
return -1;
|
|
70
|
+
}
|
|
71
|
+
return Number.parseInt(match[1], 10) * 60 + Number.parseInt(match[2], 10);
|
|
72
|
+
}
|
|
73
|
+
function compareByRecency(left, right) {
|
|
74
|
+
if (left.date !== right.date) {
|
|
75
|
+
return right.date.localeCompare(left.date);
|
|
76
|
+
}
|
|
77
|
+
return timeFromObservationText(right.text) - timeFromObservationText(left.text);
|
|
78
|
+
}
|
|
63
79
|
function formatRecentObservations(highlights) {
|
|
64
80
|
if (highlights.length === 0) {
|
|
65
81
|
return "_No critical or notable observations from today or yesterday._";
|
|
66
82
|
}
|
|
83
|
+
const sorted = [...highlights].sort(compareByRecency);
|
|
84
|
+
const red = sorted.filter((item) => item.priority === "\u{1F534}").slice(0, MAX_WAKE_RED_OBSERVATIONS);
|
|
85
|
+
const yellow = sorted.filter((item) => item.priority === "\u{1F7E1}").slice(0, MAX_WAKE_YELLOW_OBSERVATIONS);
|
|
86
|
+
const visible = [...red, ...yellow].sort(compareByRecency);
|
|
87
|
+
const omittedCount = Math.max(0, highlights.length - visible.length);
|
|
67
88
|
const byDate = /* @__PURE__ */ new Map();
|
|
68
|
-
for (const item of
|
|
89
|
+
for (const item of visible) {
|
|
69
90
|
const bucket = byDate.get(item.date) ?? [];
|
|
70
91
|
bucket.push(item);
|
|
71
92
|
byDate.set(item.date, bucket);
|
|
72
93
|
}
|
|
73
94
|
const lines = [];
|
|
95
|
+
const bodyLineBudget = Math.max(1, MAX_WAKE_OUTPUT_LINES - (omittedCount > 0 ? 1 : 0));
|
|
74
96
|
for (const [date, items] of byDate.entries()) {
|
|
97
|
+
if (lines.length >= bodyLineBudget) {
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
75
100
|
lines.push(`### ${date}`);
|
|
76
101
|
for (const item of items) {
|
|
102
|
+
if (lines.length >= bodyLineBudget) {
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
77
105
|
lines.push(`- ${item.priority} ${item.text}`);
|
|
78
106
|
}
|
|
79
|
-
lines.
|
|
107
|
+
if (lines.length < bodyLineBudget) {
|
|
108
|
+
lines.push("");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (omittedCount > 0) {
|
|
112
|
+
lines.push(`... and ${omittedCount} more observations (use \`clawvault context\` to query)`);
|
|
80
113
|
}
|
|
81
114
|
return lines.join("\n").trim();
|
|
82
115
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -333,7 +333,7 @@ declare class Compressor {
|
|
|
333
333
|
private normalizeLlmOutput;
|
|
334
334
|
/**
|
|
335
335
|
* Post-process LLM output to enforce priority rules.
|
|
336
|
-
* Lines matching
|
|
336
|
+
* Lines matching critical rules get upgraded to 🔴, notable rules to 🟡.
|
|
337
337
|
*/
|
|
338
338
|
private enforcePriorityRules;
|
|
339
339
|
private fallbackCompression;
|
|
@@ -343,6 +343,8 @@ declare class Compressor {
|
|
|
343
343
|
private parseSections;
|
|
344
344
|
private renderSections;
|
|
345
345
|
private inferPriority;
|
|
346
|
+
private isCriticalContent;
|
|
347
|
+
private isNotableContent;
|
|
346
348
|
private normalizeText;
|
|
347
349
|
private extractDate;
|
|
348
350
|
private extractTime;
|
|
@@ -368,22 +370,28 @@ declare class Reflector {
|
|
|
368
370
|
interface SessionWatcherOptions {
|
|
369
371
|
ignoreInitial?: boolean;
|
|
370
372
|
debounceMs?: number;
|
|
373
|
+
flushThresholdChars?: number;
|
|
371
374
|
}
|
|
372
375
|
declare class SessionWatcher {
|
|
373
376
|
private readonly watchPath;
|
|
374
377
|
private readonly observer;
|
|
375
378
|
private readonly ignoreInitial;
|
|
376
379
|
private readonly debounceMs;
|
|
380
|
+
private readonly flushThresholdChars;
|
|
377
381
|
private watcher;
|
|
378
382
|
private fileOffsets;
|
|
379
383
|
private pendingPaths;
|
|
380
384
|
private debounceTimer;
|
|
381
385
|
private processingQueue;
|
|
386
|
+
private bufferedChars;
|
|
382
387
|
constructor(watchPath: string, observer: Observer, options?: SessionWatcherOptions);
|
|
383
388
|
start(): Promise<void>;
|
|
384
389
|
stop(): Promise<void>;
|
|
385
390
|
private scheduleDrain;
|
|
391
|
+
private drainPendingPaths;
|
|
386
392
|
private consumeFile;
|
|
393
|
+
private primeInitialOffsets;
|
|
394
|
+
private collectFiles;
|
|
387
395
|
}
|
|
388
396
|
|
|
389
397
|
declare function parseSessionFile(filePath: string): string[];
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
contextCommand,
|
|
17
17
|
formatContextMarkdown,
|
|
18
18
|
registerContextCommand
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-UBRYOIII.js";
|
|
20
20
|
import {
|
|
21
21
|
ClawVault,
|
|
22
22
|
createVault,
|
|
@@ -41,13 +41,13 @@ import {
|
|
|
41
41
|
SessionWatcher,
|
|
42
42
|
observeCommand,
|
|
43
43
|
registerObserveCommand
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-2MP4EHJ7.js";
|
|
45
45
|
import {
|
|
46
46
|
Compressor,
|
|
47
47
|
Observer,
|
|
48
48
|
Reflector,
|
|
49
49
|
parseSessionFile
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-SIDM2I2C.js";
|
|
51
51
|
|
|
52
52
|
// src/index.ts
|
|
53
53
|
import * as fs from "fs";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawvault",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.6",
|
|
4
4
|
"description": "ClawVault™ - 🐘 An elephant never forgets. Structured memory for OpenClaw agents. Context death resilience, Obsidian-compatible markdown, local semantic search.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|