minimem 0.0.3 → 0.0.4
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-ARZQHOJI.js +239 -0
- package/dist/chunk-ARZQHOJI.js.map +1 -0
- package/dist/chunk-GVWPZRF7.js +260 -0
- package/dist/chunk-GVWPZRF7.js.map +1 -0
- package/dist/cli/index.js +1411 -612
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +375 -109
- package/dist/index.js +1330 -370
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +65 -0
- package/dist/internal.js +33 -0
- package/dist/internal.js.map +1 -0
- package/dist/session.d.ts +96 -0
- package/dist/session.js +15 -0
- package/dist/session.js.map +1 -0
- package/package.json +10 -2
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// src/internal.ts
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import fsSync from "fs";
|
|
4
|
+
import fs from "fs/promises";
|
|
5
|
+
import path from "path";
|
|
6
|
+
function logError(context, error, debug) {
|
|
7
|
+
if (!debug) return;
|
|
8
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9
|
+
debug(`[${context}] Error: ${message}`);
|
|
10
|
+
}
|
|
11
|
+
function ensureDir(dir, debug) {
|
|
12
|
+
try {
|
|
13
|
+
fsSync.mkdirSync(dir, { recursive: true });
|
|
14
|
+
} catch (error) {
|
|
15
|
+
const nodeError = error;
|
|
16
|
+
if (nodeError.code !== "EEXIST") {
|
|
17
|
+
logError("ensureDir", error, debug);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return dir;
|
|
21
|
+
}
|
|
22
|
+
function normalizeRelPath(value) {
|
|
23
|
+
const trimmed = value.trim().replace(/^[./]+/, "");
|
|
24
|
+
return trimmed.replace(/\\/g, "/");
|
|
25
|
+
}
|
|
26
|
+
function isMemoryPath(relPath) {
|
|
27
|
+
const normalized = normalizeRelPath(relPath);
|
|
28
|
+
if (!normalized) return false;
|
|
29
|
+
if (normalized === "MEMORY.md" || normalized === "memory.md") return true;
|
|
30
|
+
return normalized.startsWith("memory/");
|
|
31
|
+
}
|
|
32
|
+
async function exists(filePath) {
|
|
33
|
+
try {
|
|
34
|
+
await fs.access(filePath);
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function walkDir(dir, files) {
|
|
41
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
const full = path.join(dir, entry.name);
|
|
44
|
+
if (entry.isDirectory()) {
|
|
45
|
+
await walkDir(full, files);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (!entry.isFile()) continue;
|
|
49
|
+
if (!entry.name.endsWith(".md")) continue;
|
|
50
|
+
files.push(full);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function listMemoryFiles(memoryDir) {
|
|
54
|
+
const result = [];
|
|
55
|
+
const memoryFile = path.join(memoryDir, "MEMORY.md");
|
|
56
|
+
const altMemoryFile = path.join(memoryDir, "memory.md");
|
|
57
|
+
const hasUpper = await exists(memoryFile);
|
|
58
|
+
const hasLower = await exists(altMemoryFile);
|
|
59
|
+
if (hasUpper && hasLower) {
|
|
60
|
+
let upperReal = memoryFile;
|
|
61
|
+
let lowerReal = altMemoryFile;
|
|
62
|
+
try {
|
|
63
|
+
upperReal = await fs.realpath(memoryFile);
|
|
64
|
+
} catch {
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
lowerReal = await fs.realpath(altMemoryFile);
|
|
68
|
+
} catch {
|
|
69
|
+
}
|
|
70
|
+
if (upperReal !== lowerReal) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`Both MEMORY.md and memory.md exist in ${memoryDir}. Please remove one to avoid ambiguity.`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
result.push(memoryFile);
|
|
76
|
+
} else if (hasUpper) {
|
|
77
|
+
result.push(memoryFile);
|
|
78
|
+
} else if (hasLower) {
|
|
79
|
+
result.push(altMemoryFile);
|
|
80
|
+
}
|
|
81
|
+
const memorySubDir = path.join(memoryDir, "memory");
|
|
82
|
+
if (await exists(memorySubDir)) {
|
|
83
|
+
await walkDir(memorySubDir, result);
|
|
84
|
+
}
|
|
85
|
+
if (result.length <= 1) return result;
|
|
86
|
+
const seen = /* @__PURE__ */ new Set();
|
|
87
|
+
const deduped = [];
|
|
88
|
+
for (const entry of result) {
|
|
89
|
+
let key = entry;
|
|
90
|
+
try {
|
|
91
|
+
key = await fs.realpath(entry);
|
|
92
|
+
} catch {
|
|
93
|
+
}
|
|
94
|
+
if (seen.has(key)) continue;
|
|
95
|
+
seen.add(key);
|
|
96
|
+
deduped.push(entry);
|
|
97
|
+
}
|
|
98
|
+
return deduped;
|
|
99
|
+
}
|
|
100
|
+
function hashText(value) {
|
|
101
|
+
return crypto.createHash("sha256").update(value).digest("hex");
|
|
102
|
+
}
|
|
103
|
+
async function buildFileEntry(absPath, memoryDir) {
|
|
104
|
+
const stat = await fs.stat(absPath);
|
|
105
|
+
const content = await fs.readFile(absPath, "utf-8");
|
|
106
|
+
const hash = hashText(content);
|
|
107
|
+
return {
|
|
108
|
+
path: path.relative(memoryDir, absPath).replace(/\\/g, "/"),
|
|
109
|
+
absPath,
|
|
110
|
+
mtimeMs: stat.mtimeMs,
|
|
111
|
+
size: stat.size,
|
|
112
|
+
hash
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function stripPrivateContent(content) {
|
|
116
|
+
return content.replace(/<private>[\s\S]*?<\/private>/gi, (match) => {
|
|
117
|
+
const lineCount = match.split("\n").length;
|
|
118
|
+
return "\n".repeat(lineCount - 1);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function chunkMarkdown(content, chunking) {
|
|
122
|
+
const stripped = stripPrivateContent(content);
|
|
123
|
+
const lines = stripped.split("\n");
|
|
124
|
+
if (lines.length === 0) return [];
|
|
125
|
+
const maxChars = Math.max(32, chunking.tokens * 4);
|
|
126
|
+
const overlapChars = Math.max(0, chunking.overlap * 4);
|
|
127
|
+
const chunks = [];
|
|
128
|
+
let current = [];
|
|
129
|
+
let currentChars = 0;
|
|
130
|
+
const flush = () => {
|
|
131
|
+
if (current.length === 0) return;
|
|
132
|
+
const firstEntry = current[0];
|
|
133
|
+
const lastEntry = current[current.length - 1];
|
|
134
|
+
if (!firstEntry || !lastEntry) return;
|
|
135
|
+
const text = current.map((entry) => entry.line).join("\n");
|
|
136
|
+
const startLine = firstEntry.lineNo;
|
|
137
|
+
const endLine = lastEntry.lineNo;
|
|
138
|
+
chunks.push({
|
|
139
|
+
startLine,
|
|
140
|
+
endLine,
|
|
141
|
+
text,
|
|
142
|
+
hash: hashText(text)
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
const carryOverlap = () => {
|
|
146
|
+
if (overlapChars <= 0 || current.length === 0) {
|
|
147
|
+
current = [];
|
|
148
|
+
currentChars = 0;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
let acc = 0;
|
|
152
|
+
const kept = [];
|
|
153
|
+
for (let i = current.length - 1; i >= 0; i -= 1) {
|
|
154
|
+
const entry = current[i];
|
|
155
|
+
if (!entry) continue;
|
|
156
|
+
acc += entry.line.length + 1;
|
|
157
|
+
kept.unshift(entry);
|
|
158
|
+
if (acc >= overlapChars) break;
|
|
159
|
+
}
|
|
160
|
+
current = kept;
|
|
161
|
+
currentChars = kept.reduce((sum, entry) => sum + entry.line.length + 1, 0);
|
|
162
|
+
};
|
|
163
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
164
|
+
const line = lines[i] ?? "";
|
|
165
|
+
const lineNo = i + 1;
|
|
166
|
+
const segments = [];
|
|
167
|
+
if (line.length === 0) {
|
|
168
|
+
segments.push("");
|
|
169
|
+
} else {
|
|
170
|
+
for (let start = 0; start < line.length; start += maxChars) {
|
|
171
|
+
segments.push(line.slice(start, start + maxChars));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
for (const segment of segments) {
|
|
175
|
+
const lineSize = segment.length + 1;
|
|
176
|
+
if (currentChars + lineSize > maxChars && current.length > 0) {
|
|
177
|
+
flush();
|
|
178
|
+
carryOverlap();
|
|
179
|
+
}
|
|
180
|
+
current.push({ line: segment, lineNo });
|
|
181
|
+
currentChars += lineSize;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
flush();
|
|
185
|
+
return chunks;
|
|
186
|
+
}
|
|
187
|
+
function extractChunkMetadata(text) {
|
|
188
|
+
const typeMatch = text.match(/<!--\s*type:\s*([\w-]+)\s*-->/i);
|
|
189
|
+
return typeMatch ? { type: typeMatch[1].toLowerCase() } : {};
|
|
190
|
+
}
|
|
191
|
+
function parseEmbedding(raw) {
|
|
192
|
+
try {
|
|
193
|
+
const parsed = JSON.parse(raw);
|
|
194
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
195
|
+
} catch {
|
|
196
|
+
return [];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function cosineSimilarity(a, b) {
|
|
200
|
+
if (a.length === 0 || b.length === 0) return 0;
|
|
201
|
+
const len = Math.min(a.length, b.length);
|
|
202
|
+
let dot = 0;
|
|
203
|
+
let normA = 0;
|
|
204
|
+
let normB = 0;
|
|
205
|
+
for (let i = 0; i < len; i += 1) {
|
|
206
|
+
const av = a[i] ?? 0;
|
|
207
|
+
const bv = b[i] ?? 0;
|
|
208
|
+
dot += av * bv;
|
|
209
|
+
normA += av * av;
|
|
210
|
+
normB += bv * bv;
|
|
211
|
+
}
|
|
212
|
+
if (normA === 0 || normB === 0) return 0;
|
|
213
|
+
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
214
|
+
}
|
|
215
|
+
function truncateUtf16Safe(text, maxChars) {
|
|
216
|
+
if (text.length <= maxChars) return text;
|
|
217
|
+
return text.slice(0, maxChars);
|
|
218
|
+
}
|
|
219
|
+
function vectorToBlob(embedding) {
|
|
220
|
+
return Buffer.from(new Float32Array(embedding).buffer);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export {
|
|
224
|
+
logError,
|
|
225
|
+
ensureDir,
|
|
226
|
+
normalizeRelPath,
|
|
227
|
+
isMemoryPath,
|
|
228
|
+
listMemoryFiles,
|
|
229
|
+
hashText,
|
|
230
|
+
buildFileEntry,
|
|
231
|
+
stripPrivateContent,
|
|
232
|
+
chunkMarkdown,
|
|
233
|
+
extractChunkMetadata,
|
|
234
|
+
parseEmbedding,
|
|
235
|
+
cosineSimilarity,
|
|
236
|
+
truncateUtf16Safe,
|
|
237
|
+
vectorToBlob
|
|
238
|
+
};
|
|
239
|
+
//# sourceMappingURL=chunk-ARZQHOJI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport fsSync from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/**\n * Debug function type for optional logging\n */\nexport type DebugFn = (message: string, data?: Record<string, unknown>) => void;\n\n/**\n * Log an error with context (for debugging).\n * Only logs if a debug function is provided.\n *\n * @param context - A short identifier for where the error occurred\n * @param error - The error object or message\n * @param debug - Optional debug function to log to\n */\nexport function logError(\n context: string,\n error: unknown,\n debug?: DebugFn\n): void {\n if (!debug) return;\n\n const message = error instanceof Error ? error.message : String(error);\n debug(`[${context}] Error: ${message}`);\n}\n\nexport type MemoryFileEntry = {\n path: string;\n absPath: string;\n mtimeMs: number;\n size: number;\n hash: string;\n};\n\nexport type MemoryChunk = {\n startLine: number;\n endLine: number;\n text: string;\n hash: string;\n};\n\n/**\n * Ensure a directory exists, creating it if necessary.\n *\n * @param dir - The directory path to ensure exists\n * @param debug - Optional debug function for logging errors\n * @returns The directory path\n */\nexport function ensureDir(dir: string, debug?: DebugFn): string {\n try {\n fsSync.mkdirSync(dir, { recursive: true });\n } catch (error) {\n // Only swallow EEXIST errors (directory already exists)\n // Log other errors for debugging\n const nodeError = error as NodeJS.ErrnoException;\n if (nodeError.code !== \"EEXIST\") {\n logError(\"ensureDir\", error, debug);\n }\n }\n return dir;\n}\n\nexport function normalizeRelPath(value: string): string {\n const trimmed = value.trim().replace(/^[./]+/, \"\");\n return trimmed.replace(/\\\\/g, \"/\");\n}\n\nexport function isMemoryPath(relPath: string): boolean {\n const normalized = normalizeRelPath(relPath);\n if (!normalized) return false;\n if (normalized === \"MEMORY.md\" || normalized === \"memory.md\") return true;\n return normalized.startsWith(\"memory/\");\n}\n\nasync function exists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function walkDir(dir: string, files: string[]) {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n await walkDir(full, files);\n continue;\n }\n if (!entry.isFile()) continue;\n if (!entry.name.endsWith(\".md\")) continue;\n files.push(full);\n }\n}\n\nexport async function listMemoryFiles(memoryDir: string): Promise<string[]> {\n const result: string[] = [];\n const memoryFile = path.join(memoryDir, \"MEMORY.md\");\n const altMemoryFile = path.join(memoryDir, \"memory.md\");\n const hasUpper = await exists(memoryFile);\n const hasLower = await exists(altMemoryFile);\n\n // Prevent ambiguity: both MEMORY.md and memory.md cannot coexist\n // (unless they resolve to the same file on case-insensitive filesystems)\n if (hasUpper && hasLower) {\n let upperReal = memoryFile;\n let lowerReal = altMemoryFile;\n try { upperReal = await fs.realpath(memoryFile); } catch {}\n try { lowerReal = await fs.realpath(altMemoryFile); } catch {}\n if (upperReal !== lowerReal) {\n throw new Error(\n `Both MEMORY.md and memory.md exist in ${memoryDir}. ` +\n `Please remove one to avoid ambiguity.`\n );\n }\n // Same file (case-insensitive FS) — only include once\n result.push(memoryFile);\n } else if (hasUpper) {\n result.push(memoryFile);\n } else if (hasLower) {\n result.push(altMemoryFile);\n }\n\n const memorySubDir = path.join(memoryDir, \"memory\");\n if (await exists(memorySubDir)) {\n await walkDir(memorySubDir, result);\n }\n if (result.length <= 1) return result;\n const seen = new Set<string>();\n const deduped: string[] = [];\n for (const entry of result) {\n let key = entry;\n try {\n key = await fs.realpath(entry);\n } catch {}\n if (seen.has(key)) continue;\n seen.add(key);\n deduped.push(entry);\n }\n return deduped;\n}\n\nexport function hashText(value: string): string {\n return crypto.createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nexport async function buildFileEntry(\n absPath: string,\n memoryDir: string,\n): Promise<MemoryFileEntry> {\n const stat = await fs.stat(absPath);\n const content = await fs.readFile(absPath, \"utf-8\");\n const hash = hashText(content);\n return {\n path: path.relative(memoryDir, absPath).replace(/\\\\/g, \"/\"),\n absPath,\n mtimeMs: stat.mtimeMs,\n size: stat.size,\n hash,\n };\n}\n\n/**\n * Strip content between <private>...</private> tags.\n * Replaces private blocks with the same number of empty lines to preserve line numbering.\n */\nexport function stripPrivateContent(content: string): string {\n return content.replace(/<private>[\\s\\S]*?<\\/private>/gi, (match) => {\n const lineCount = match.split(\"\\n\").length;\n return \"\\n\".repeat(lineCount - 1);\n });\n}\n\nexport function chunkMarkdown(\n content: string,\n chunking: { tokens: number; overlap: number },\n): MemoryChunk[] {\n const stripped = stripPrivateContent(content);\n const lines = stripped.split(\"\\n\");\n if (lines.length === 0) return [];\n const maxChars = Math.max(32, chunking.tokens * 4);\n const overlapChars = Math.max(0, chunking.overlap * 4);\n const chunks: MemoryChunk[] = [];\n\n let current: Array<{ line: string; lineNo: number }> = [];\n let currentChars = 0;\n\n const flush = () => {\n if (current.length === 0) return;\n const firstEntry = current[0];\n const lastEntry = current[current.length - 1];\n if (!firstEntry || !lastEntry) return;\n const text = current.map((entry) => entry.line).join(\"\\n\");\n const startLine = firstEntry.lineNo;\n const endLine = lastEntry.lineNo;\n chunks.push({\n startLine,\n endLine,\n text,\n hash: hashText(text),\n });\n };\n\n const carryOverlap = () => {\n if (overlapChars <= 0 || current.length === 0) {\n current = [];\n currentChars = 0;\n return;\n }\n let acc = 0;\n const kept: Array<{ line: string; lineNo: number }> = [];\n for (let i = current.length - 1; i >= 0; i -= 1) {\n const entry = current[i];\n if (!entry) continue;\n acc += entry.line.length + 1;\n kept.unshift(entry);\n if (acc >= overlapChars) break;\n }\n current = kept;\n currentChars = kept.reduce((sum, entry) => sum + entry.line.length + 1, 0);\n };\n\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i] ?? \"\";\n const lineNo = i + 1;\n const segments: string[] = [];\n if (line.length === 0) {\n segments.push(\"\");\n } else {\n for (let start = 0; start < line.length; start += maxChars) {\n segments.push(line.slice(start, start + maxChars));\n }\n }\n for (const segment of segments) {\n const lineSize = segment.length + 1;\n if (currentChars + lineSize > maxChars && current.length > 0) {\n flush();\n carryOverlap();\n }\n current.push({ line: segment, lineNo });\n currentChars += lineSize;\n }\n }\n flush();\n return chunks;\n}\n\n/**\n * Extract metadata from a chunk's text content.\n * Looks for HTML comments like <!-- type: decision --> in the chunk.\n */\nexport function extractChunkMetadata(text: string): { type?: string } {\n const typeMatch = text.match(/<!--\\s*type:\\s*([\\w-]+)\\s*-->/i);\n return typeMatch ? { type: typeMatch[1].toLowerCase() } : {};\n}\n\nexport function parseEmbedding(raw: string): number[] {\n try {\n const parsed = JSON.parse(raw) as number[];\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n}\n\nexport function cosineSimilarity(a: number[], b: number[]): number {\n if (a.length === 0 || b.length === 0) return 0;\n const len = Math.min(a.length, b.length);\n let dot = 0;\n let normA = 0;\n let normB = 0;\n for (let i = 0; i < len; i += 1) {\n const av = a[i] ?? 0;\n const bv = b[i] ?? 0;\n dot += av * bv;\n normA += av * av;\n normB += bv * bv;\n }\n if (normA === 0 || normB === 0) return 0;\n return dot / (Math.sqrt(normA) * Math.sqrt(normB));\n}\n\nexport function truncateUtf16Safe(text: string, maxChars: number): string {\n if (text.length <= maxChars) return text;\n return text.slice(0, maxChars);\n}\n\n/**\n * Convert a numeric embedding vector to a Buffer suitable for sqlite-vec storage.\n * Uses Float32 encoding, which is the format expected by sqlite-vec's vector functions.\n */\nexport function vectorToBlob(embedding: number[]): Buffer {\n return Buffer.from(new Float32Array(embedding).buffer);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// src/session.ts
|
|
2
|
+
import * as os from "os";
|
|
3
|
+
function parseFrontmatter(content) {
|
|
4
|
+
const frontmatterRegex = /^---\n([\s\S]*?)\n---\n/;
|
|
5
|
+
const match = content.match(frontmatterRegex);
|
|
6
|
+
if (!match) {
|
|
7
|
+
return { frontmatter: void 0, body: content };
|
|
8
|
+
}
|
|
9
|
+
const yamlContent = match[1];
|
|
10
|
+
const body = content.slice(match[0].length);
|
|
11
|
+
try {
|
|
12
|
+
const frontmatter = parseSimpleYaml(yamlContent);
|
|
13
|
+
return { frontmatter, body };
|
|
14
|
+
} catch {
|
|
15
|
+
return { frontmatter: void 0, body: content };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function parseSimpleYaml(yaml) {
|
|
19
|
+
const lines = yaml.split("\n");
|
|
20
|
+
return parseYamlBlock(lines, 0, 0, lines.length).value;
|
|
21
|
+
}
|
|
22
|
+
function parseYamlBlock(lines, indent, startIdx, endIdx) {
|
|
23
|
+
const result = {};
|
|
24
|
+
let i = startIdx;
|
|
25
|
+
while (i < endIdx) {
|
|
26
|
+
const line = lines[i];
|
|
27
|
+
if (!line || !line.trim()) {
|
|
28
|
+
i++;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const lineIndent = getIndent(line);
|
|
32
|
+
if (lineIndent < indent) break;
|
|
33
|
+
if (lineIndent > indent) {
|
|
34
|
+
i++;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const keyMatch = line.match(/^(\s*)([\w-]+):\s*(.*)?$/);
|
|
38
|
+
if (!keyMatch) {
|
|
39
|
+
i++;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const [, , key, rawValue] = keyMatch;
|
|
43
|
+
const value = rawValue?.trim() ?? "";
|
|
44
|
+
if (value === "" || value === void 0) {
|
|
45
|
+
const nextNonEmpty = findNextNonEmptyLine(lines, i + 1, endIdx);
|
|
46
|
+
if (nextNonEmpty < endIdx) {
|
|
47
|
+
const nextLine = lines[nextNonEmpty];
|
|
48
|
+
const nextIndent = getIndent(nextLine);
|
|
49
|
+
if (nextIndent > indent) {
|
|
50
|
+
if (nextLine.trimStart().startsWith("- ")) {
|
|
51
|
+
const listResult = parseYamlList(lines, nextIndent, i + 1, endIdx);
|
|
52
|
+
result[key] = listResult.value;
|
|
53
|
+
i = listResult.nextIdx;
|
|
54
|
+
} else {
|
|
55
|
+
const blockResult = parseYamlBlock(lines, nextIndent, i + 1, endIdx);
|
|
56
|
+
result[key] = blockResult.value;
|
|
57
|
+
i = blockResult.nextIdx;
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
result[key] = null;
|
|
63
|
+
i++;
|
|
64
|
+
} else {
|
|
65
|
+
result[key] = parseYamlValue(value);
|
|
66
|
+
i++;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { value: result, nextIdx: i };
|
|
70
|
+
}
|
|
71
|
+
function parseYamlList(lines, indent, startIdx, endIdx) {
|
|
72
|
+
const result = [];
|
|
73
|
+
let i = startIdx;
|
|
74
|
+
while (i < endIdx) {
|
|
75
|
+
const line = lines[i];
|
|
76
|
+
if (!line || !line.trim()) {
|
|
77
|
+
i++;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const lineIndent = getIndent(line);
|
|
81
|
+
if (lineIndent < indent) break;
|
|
82
|
+
if (lineIndent > indent) {
|
|
83
|
+
i++;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const trimmed = line.trimStart();
|
|
87
|
+
if (!trimmed.startsWith("- ")) break;
|
|
88
|
+
const itemContent = trimmed.slice(2).trim();
|
|
89
|
+
if (itemContent === "" || itemContent === void 0) {
|
|
90
|
+
const nextNonEmpty = findNextNonEmptyLine(lines, i + 1, endIdx);
|
|
91
|
+
if (nextNonEmpty < endIdx) {
|
|
92
|
+
const nextIndent = getIndent(lines[nextNonEmpty]);
|
|
93
|
+
if (nextIndent > indent) {
|
|
94
|
+
const blockResult = parseYamlBlock(lines, nextIndent, i + 1, endIdx);
|
|
95
|
+
result.push(blockResult.value);
|
|
96
|
+
i = blockResult.nextIdx;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
result.push(null);
|
|
101
|
+
i++;
|
|
102
|
+
} else {
|
|
103
|
+
const kvMatch = itemContent.match(/^([\w-]+):\s*(.*)$/);
|
|
104
|
+
if (kvMatch) {
|
|
105
|
+
const obj = {};
|
|
106
|
+
const [, firstKey, firstVal] = kvMatch;
|
|
107
|
+
obj[firstKey] = parseYamlValue(firstVal?.trim() ?? "");
|
|
108
|
+
const itemKeyIndent = indent + 2;
|
|
109
|
+
let j = i + 1;
|
|
110
|
+
while (j < endIdx) {
|
|
111
|
+
const nextLine = lines[j];
|
|
112
|
+
if (!nextLine || !nextLine.trim()) {
|
|
113
|
+
j++;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const nextLineIndent = getIndent(nextLine);
|
|
117
|
+
if (nextLineIndent < itemKeyIndent) break;
|
|
118
|
+
if (nextLineIndent === itemKeyIndent) {
|
|
119
|
+
const nextKv = nextLine.match(/^\s*([\w-]+):\s*(.*)$/);
|
|
120
|
+
if (nextKv) {
|
|
121
|
+
const [, nk, nv] = nextKv;
|
|
122
|
+
obj[nk] = parseYamlValue(nv?.trim() ?? "");
|
|
123
|
+
j++;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
result.push(obj);
|
|
130
|
+
i = j;
|
|
131
|
+
} else {
|
|
132
|
+
result.push(parseYamlValue(itemContent));
|
|
133
|
+
i++;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return { value: result, nextIdx: i };
|
|
138
|
+
}
|
|
139
|
+
function getIndent(line) {
|
|
140
|
+
const match = line.match(/^(\s*)/);
|
|
141
|
+
return match ? match[1].length : 0;
|
|
142
|
+
}
|
|
143
|
+
function findNextNonEmptyLine(lines, from, end) {
|
|
144
|
+
for (let i = from; i < end; i++) {
|
|
145
|
+
if (lines[i]?.trim()) return i;
|
|
146
|
+
}
|
|
147
|
+
return end;
|
|
148
|
+
}
|
|
149
|
+
function parseYamlValue(value) {
|
|
150
|
+
if (value === "") return null;
|
|
151
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
152
|
+
return value.slice(1, -1);
|
|
153
|
+
}
|
|
154
|
+
if (value === "null" || value === "~") return null;
|
|
155
|
+
if (value === "true") return true;
|
|
156
|
+
if (value === "false") return false;
|
|
157
|
+
const num = Number(value);
|
|
158
|
+
if (!isNaN(num) && value !== "") return num;
|
|
159
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
160
|
+
const inner = value.slice(1, -1);
|
|
161
|
+
if (inner.trim() === "") return [];
|
|
162
|
+
return inner.split(",").map((s) => parseYamlValue(s.trim()));
|
|
163
|
+
}
|
|
164
|
+
return value;
|
|
165
|
+
}
|
|
166
|
+
function serializeFrontmatter(frontmatter) {
|
|
167
|
+
const lines = ["---"];
|
|
168
|
+
if (frontmatter.id) {
|
|
169
|
+
lines.push(`id: ${frontmatter.id}`);
|
|
170
|
+
}
|
|
171
|
+
if (frontmatter.type) {
|
|
172
|
+
lines.push(`type: ${frontmatter.type}`);
|
|
173
|
+
}
|
|
174
|
+
if (frontmatter.session) {
|
|
175
|
+
lines.push("session:");
|
|
176
|
+
const session = frontmatter.session;
|
|
177
|
+
if (session.id) lines.push(` id: ${session.id}`);
|
|
178
|
+
if (session.source) lines.push(` source: ${session.source}`);
|
|
179
|
+
if (session.project) lines.push(` project: ${formatPath(session.project)}`);
|
|
180
|
+
if (session.transcript) lines.push(` transcript: ${formatPath(session.transcript)}`);
|
|
181
|
+
}
|
|
182
|
+
if (frontmatter.created) {
|
|
183
|
+
lines.push(`created: ${frontmatter.created}`);
|
|
184
|
+
}
|
|
185
|
+
if (frontmatter.updated) {
|
|
186
|
+
lines.push(`updated: ${frontmatter.updated}`);
|
|
187
|
+
}
|
|
188
|
+
if (frontmatter.tags && frontmatter.tags.length > 0) {
|
|
189
|
+
lines.push(`tags: [${frontmatter.tags.join(", ")}]`);
|
|
190
|
+
}
|
|
191
|
+
if (frontmatter.domain && frontmatter.domain.length > 0) {
|
|
192
|
+
lines.push(`domain: [${frontmatter.domain.join(", ")}]`);
|
|
193
|
+
}
|
|
194
|
+
if (frontmatter.entities && frontmatter.entities.length > 0) {
|
|
195
|
+
lines.push(`entities: [${frontmatter.entities.join(", ")}]`);
|
|
196
|
+
}
|
|
197
|
+
if (frontmatter.confidence !== void 0) {
|
|
198
|
+
lines.push(`confidence: ${frontmatter.confidence}`);
|
|
199
|
+
}
|
|
200
|
+
if (frontmatter.source) {
|
|
201
|
+
lines.push("source:");
|
|
202
|
+
if (frontmatter.source.origin) lines.push(` origin: ${frontmatter.source.origin}`);
|
|
203
|
+
if (frontmatter.source.trajectories && frontmatter.source.trajectories.length > 0) {
|
|
204
|
+
lines.push(` trajectories: [${frontmatter.source.trajectories.join(", ")}]`);
|
|
205
|
+
}
|
|
206
|
+
if (frontmatter.source.agentId) lines.push(` agentId: ${frontmatter.source.agentId}`);
|
|
207
|
+
}
|
|
208
|
+
if (frontmatter.links && frontmatter.links.length > 0) {
|
|
209
|
+
lines.push("links:");
|
|
210
|
+
for (const link of frontmatter.links) {
|
|
211
|
+
lines.push(` - target: ${link.target}`);
|
|
212
|
+
lines.push(` relation: ${link.relation}`);
|
|
213
|
+
if (link.layer) lines.push(` layer: ${link.layer}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (frontmatter.supersedes !== void 0) {
|
|
217
|
+
lines.push(`supersedes: ${frontmatter.supersedes === null ? "~" : frontmatter.supersedes}`);
|
|
218
|
+
}
|
|
219
|
+
lines.push("---");
|
|
220
|
+
return lines.join("\n") + "\n";
|
|
221
|
+
}
|
|
222
|
+
function addFrontmatter(content, frontmatter) {
|
|
223
|
+
const { frontmatter: existing, body } = parseFrontmatter(content);
|
|
224
|
+
const merged = {
|
|
225
|
+
...existing,
|
|
226
|
+
...frontmatter,
|
|
227
|
+
session: {
|
|
228
|
+
...existing?.session,
|
|
229
|
+
...frontmatter.session
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
if (!merged.created) {
|
|
233
|
+
merged.created = (/* @__PURE__ */ new Date()).toISOString();
|
|
234
|
+
}
|
|
235
|
+
merged.updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
236
|
+
return serializeFrontmatter(merged) + body;
|
|
237
|
+
}
|
|
238
|
+
function addSessionToContent(content, session) {
|
|
239
|
+
return addFrontmatter(content, { session });
|
|
240
|
+
}
|
|
241
|
+
function formatPath(filePath) {
|
|
242
|
+
const home = os.homedir();
|
|
243
|
+
if (filePath.startsWith(home)) {
|
|
244
|
+
return "~" + filePath.slice(home.length);
|
|
245
|
+
}
|
|
246
|
+
return filePath;
|
|
247
|
+
}
|
|
248
|
+
function extractSession(content) {
|
|
249
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
250
|
+
return frontmatter?.session;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export {
|
|
254
|
+
parseFrontmatter,
|
|
255
|
+
serializeFrontmatter,
|
|
256
|
+
addFrontmatter,
|
|
257
|
+
addSessionToContent,
|
|
258
|
+
extractSession
|
|
259
|
+
};
|
|
260
|
+
//# sourceMappingURL=chunk-GVWPZRF7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/session.ts"],"sourcesContent":["/**\n * Session tracking for memory entries\n *\n * Captures context about the originating session (Claude Code, VS Code, etc.)\n * and stores it as YAML frontmatter in memory files.\n */\n\nimport * as os from \"node:os\";\n\n/**\n * Session metadata for memory entries\n */\nexport type SessionContext = {\n /** Session identifier (e.g., Claude Code session ID) */\n id?: string;\n /** Source application (claude-code, vscode, cursor, etc.) */\n source?: string;\n /** Project directory path */\n project?: string;\n /** Path to session transcript/log file */\n transcript?: string;\n};\n\n/**\n * Source provenance for a knowledge entry\n */\nexport type KnowledgeSource = {\n origin?: string;\n trajectories?: string[];\n agentId?: string;\n};\n\n/**\n * A directional link from this entry to another knowledge node\n */\nexport type KnowledgeLink = {\n target: string;\n relation: string;\n layer?: string;\n};\n\n/**\n * Frontmatter structure for memory files\n */\nexport type MemoryFrontmatter = {\n session?: SessionContext;\n created?: string;\n updated?: string;\n tags?: string[];\n /** Knowledge node identifier */\n id?: string;\n /** Knowledge entry type */\n type?: \"observation\" | \"entity\" | \"domain-summary\" | string;\n /** Domain tags for this knowledge entry */\n domain?: string[];\n /** Entity references in this knowledge entry */\n entities?: string[];\n /** Confidence score 0-1 */\n confidence?: number;\n /** Source provenance */\n source?: KnowledgeSource;\n /** Links to other knowledge nodes */\n links?: KnowledgeLink[];\n /** ID of the entry this supersedes */\n supersedes?: string | null;\n};\n\n/**\n * Parse YAML frontmatter from content\n *\n * Frontmatter is delimited by --- at the start and end:\n * ```\n * ---\n * session:\n * id: abc123\n * source: claude-code\n * created: 2024-01-27T14:30:00Z\n * ---\n * Actual content here...\n * ```\n */\nexport function parseFrontmatter(content: string): {\n frontmatter: MemoryFrontmatter | undefined;\n body: string;\n} {\n const frontmatterRegex = /^---\\n([\\s\\S]*?)\\n---\\n/;\n const match = content.match(frontmatterRegex);\n\n if (!match) {\n return { frontmatter: undefined, body: content };\n }\n\n const yamlContent = match[1];\n const body = content.slice(match[0].length);\n\n try {\n const frontmatter = parseSimpleYaml(yamlContent);\n return { frontmatter, body };\n } catch {\n // If parsing fails, treat as no frontmatter\n return { frontmatter: undefined, body: content };\n }\n}\n\n/**\n * Simple YAML parser for frontmatter.\n *\n * **Limitations** (by design — keeps the dependency count at zero):\n * - Does not handle multi-line strings (block scalars `|` / `>`)\n * - Does not preserve comments\n * - Keys must be simple `\\w+` identifiers (no quoted or special-char keys)\n *\n * Supports:\n * - Multi-level nesting (objects within objects)\n * - Inline arrays `[a, b]`\n * - YAML list items with `- ` syntax (including `- {key: val}` objects)\n * - Null values via `~` or `null`\n */\nfunction parseSimpleYaml(yaml: string): MemoryFrontmatter {\n const lines = yaml.split(\"\\n\");\n return parseYamlBlock(lines, 0, 0, lines.length).value as MemoryFrontmatter;\n}\n\n/**\n * Parse a block of YAML lines at a given indentation level into an object.\n * Returns the parsed value and the line index where parsing stopped.\n */\nfunction parseYamlBlock(\n lines: string[],\n indent: number,\n startIdx: number,\n endIdx: number,\n): { value: Record<string, unknown>; nextIdx: number } {\n const result: Record<string, unknown> = {};\n let i = startIdx;\n\n while (i < endIdx) {\n const line = lines[i];\n if (!line || !line.trim()) {\n i++;\n continue;\n }\n\n const lineIndent = getIndent(line);\n // If we've dedented back beyond our level, stop\n if (lineIndent < indent) break;\n // Skip lines indented deeper than expected (shouldn't happen at top of block)\n if (lineIndent > indent) {\n i++;\n continue;\n }\n\n // Match a key: value line\n const keyMatch = line.match(/^(\\s*)([\\w-]+):\\s*(.*)?$/);\n if (!keyMatch) {\n i++;\n continue;\n }\n\n const [, , key, rawValue] = keyMatch;\n const value = rawValue?.trim() ?? \"\";\n\n if (value === \"\" || value === undefined) {\n // Could be an object or a list starting on next lines\n const nextNonEmpty = findNextNonEmptyLine(lines, i + 1, endIdx);\n if (nextNonEmpty < endIdx) {\n const nextLine = lines[nextNonEmpty]!;\n const nextIndent = getIndent(nextLine);\n if (nextIndent > indent) {\n // Check if it's a list (starts with \"- \")\n if (nextLine.trimStart().startsWith(\"- \")) {\n const listResult = parseYamlList(lines, nextIndent, i + 1, endIdx);\n result[key] = listResult.value;\n i = listResult.nextIdx;\n } else {\n // Nested object\n const blockResult = parseYamlBlock(lines, nextIndent, i + 1, endIdx);\n result[key] = blockResult.value;\n i = blockResult.nextIdx;\n }\n continue;\n }\n }\n // Empty value, no nested content\n result[key] = null;\n i++;\n } else {\n result[key] = parseYamlValue(value);\n i++;\n }\n }\n\n return { value: result, nextIdx: i };\n}\n\n/**\n * Parse a YAML list (lines starting with \"- \") at a given indentation level.\n */\nfunction parseYamlList(\n lines: string[],\n indent: number,\n startIdx: number,\n endIdx: number,\n): { value: unknown[]; nextIdx: number } {\n const result: unknown[] = [];\n let i = startIdx;\n\n while (i < endIdx) {\n const line = lines[i];\n if (!line || !line.trim()) {\n i++;\n continue;\n }\n\n const lineIndent = getIndent(line);\n if (lineIndent < indent) break;\n if (lineIndent > indent) {\n i++;\n continue;\n }\n\n const trimmed = line.trimStart();\n if (!trimmed.startsWith(\"- \")) break;\n\n // Get content after \"- \"\n const itemContent = trimmed.slice(2).trim();\n\n if (itemContent === \"\" || itemContent === undefined) {\n // Sub-block under this list item\n const nextNonEmpty = findNextNonEmptyLine(lines, i + 1, endIdx);\n if (nextNonEmpty < endIdx) {\n const nextIndent = getIndent(lines[nextNonEmpty]!);\n if (nextIndent > indent) {\n const blockResult = parseYamlBlock(lines, nextIndent, i + 1, endIdx);\n result.push(blockResult.value);\n i = blockResult.nextIdx;\n continue;\n }\n }\n result.push(null);\n i++;\n } else {\n // Check for inline key: value (object item like \"- target: foo\")\n const kvMatch = itemContent.match(/^([\\w-]+):\\s*(.*)$/);\n if (kvMatch) {\n // This list item is an object — collect all keys at this item's indent + 2\n const obj: Record<string, unknown> = {};\n const [, firstKey, firstVal] = kvMatch;\n obj[firstKey] = parseYamlValue(firstVal?.trim() ?? \"\");\n\n // Look for continuation keys indented further\n const itemKeyIndent = indent + 2;\n let j = i + 1;\n while (j < endIdx) {\n const nextLine = lines[j];\n if (!nextLine || !nextLine.trim()) {\n j++;\n continue;\n }\n const nextLineIndent = getIndent(nextLine);\n if (nextLineIndent < itemKeyIndent) break;\n if (nextLineIndent === itemKeyIndent) {\n const nextKv = nextLine.match(/^\\s*([\\w-]+):\\s*(.*)$/);\n if (nextKv) {\n const [, nk, nv] = nextKv;\n obj[nk] = parseYamlValue(nv?.trim() ?? \"\");\n j++;\n continue;\n }\n }\n break;\n }\n result.push(obj);\n i = j;\n } else {\n result.push(parseYamlValue(itemContent));\n i++;\n }\n }\n }\n\n return { value: result, nextIdx: i };\n}\n\nfunction getIndent(line: string): number {\n const match = line.match(/^(\\s*)/);\n return match ? match[1].length : 0;\n}\n\nfunction findNextNonEmptyLine(lines: string[], from: number, end: number): number {\n for (let i = from; i < end; i++) {\n if (lines[i]?.trim()) return i;\n }\n return end;\n}\n\n/**\n * Parse a YAML value (handles strings, numbers, booleans, null, arrays)\n */\nfunction parseYamlValue(value: string): unknown {\n // Empty string\n if (value === \"\") return null;\n\n // Remove quotes if present\n if ((value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n return value.slice(1, -1);\n }\n\n // Null\n if (value === \"null\" || value === \"~\") return null;\n\n // Boolean\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n\n // Number\n const num = Number(value);\n if (!isNaN(num) && value !== \"\") return num;\n\n // Array (simple inline format)\n if (value.startsWith(\"[\") && value.endsWith(\"]\")) {\n const inner = value.slice(1, -1);\n if (inner.trim() === \"\") return [];\n return inner.split(\",\").map((s) => parseYamlValue(s.trim()));\n }\n\n // String\n return value;\n}\n\n/**\n * Serialize frontmatter to YAML string\n */\nexport function serializeFrontmatter(frontmatter: MemoryFrontmatter): string {\n const lines: string[] = [\"---\"];\n\n if (frontmatter.id) {\n lines.push(`id: ${frontmatter.id}`);\n }\n\n if (frontmatter.type) {\n lines.push(`type: ${frontmatter.type}`);\n }\n\n if (frontmatter.session) {\n lines.push(\"session:\");\n const session = frontmatter.session;\n if (session.id) lines.push(` id: ${session.id}`);\n if (session.source) lines.push(` source: ${session.source}`);\n if (session.project) lines.push(` project: ${formatPath(session.project)}`);\n if (session.transcript) lines.push(` transcript: ${formatPath(session.transcript)}`);\n }\n\n if (frontmatter.created) {\n lines.push(`created: ${frontmatter.created}`);\n }\n\n if (frontmatter.updated) {\n lines.push(`updated: ${frontmatter.updated}`);\n }\n\n if (frontmatter.tags && frontmatter.tags.length > 0) {\n lines.push(`tags: [${frontmatter.tags.join(\", \")}]`);\n }\n\n if (frontmatter.domain && frontmatter.domain.length > 0) {\n lines.push(`domain: [${frontmatter.domain.join(\", \")}]`);\n }\n\n if (frontmatter.entities && frontmatter.entities.length > 0) {\n lines.push(`entities: [${frontmatter.entities.join(\", \")}]`);\n }\n\n if (frontmatter.confidence !== undefined) {\n lines.push(`confidence: ${frontmatter.confidence}`);\n }\n\n if (frontmatter.source) {\n lines.push(\"source:\");\n if (frontmatter.source.origin) lines.push(` origin: ${frontmatter.source.origin}`);\n if (frontmatter.source.trajectories && frontmatter.source.trajectories.length > 0) {\n lines.push(` trajectories: [${frontmatter.source.trajectories.join(\", \")}]`);\n }\n if (frontmatter.source.agentId) lines.push(` agentId: ${frontmatter.source.agentId}`);\n }\n\n if (frontmatter.links && frontmatter.links.length > 0) {\n lines.push(\"links:\");\n for (const link of frontmatter.links) {\n lines.push(` - target: ${link.target}`);\n lines.push(` relation: ${link.relation}`);\n if (link.layer) lines.push(` layer: ${link.layer}`);\n }\n }\n\n if (frontmatter.supersedes !== undefined) {\n lines.push(`supersedes: ${frontmatter.supersedes === null ? \"~\" : frontmatter.supersedes}`);\n }\n\n lines.push(\"---\");\n return lines.join(\"\\n\") + \"\\n\";\n}\n\n/**\n * Add or update frontmatter in content\n */\nexport function addFrontmatter(\n content: string,\n frontmatter: MemoryFrontmatter,\n): string {\n const { frontmatter: existing, body } = parseFrontmatter(content);\n\n // Merge with existing frontmatter\n const merged: MemoryFrontmatter = {\n ...existing,\n ...frontmatter,\n session: {\n ...existing?.session,\n ...frontmatter.session,\n },\n };\n\n // Update timestamp\n if (!merged.created) {\n merged.created = new Date().toISOString();\n }\n merged.updated = new Date().toISOString();\n\n return serializeFrontmatter(merged) + body;\n}\n\n/**\n * Add session context as frontmatter to content\n */\nexport function addSessionToContent(\n content: string,\n session: SessionContext,\n): string {\n return addFrontmatter(content, { session });\n}\n\n/**\n * Format path for display (use ~ for home directory)\n */\nfunction formatPath(filePath: string): string {\n const home = os.homedir();\n if (filePath.startsWith(home)) {\n return \"~\" + filePath.slice(home.length);\n }\n return filePath;\n}\n\n/**\n * Extract session context from file content\n */\nexport function extractSession(content: string): SessionContext | undefined {\n const { frontmatter } = parseFrontmatter(content);\n return frontmatter?.session;\n}\n"],"mappings":"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","names":[]}
|