claude-mem-lite 2.71.0 → 2.71.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/cli.mjs +1 -1
- package/lib/import-jsonl.mjs +39 -4
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const CLI_COMMANDS = new Set(['search', 'recent', 'recall', 'get', 'timeline', 'save', 'stats', 'context', 'browse', 'delete', 'update', 'export', 'compress', 'maintain', 'optimize', 'fts-check', 'registry', 'import', 'enrich', 'activity', 'adopt', 'unadopt', 'memdir-audit', 'defer', 'help']);
|
|
2
|
+
const CLI_COMMANDS = new Set(['search', 'recent', 'recall', 'get', 'timeline', 'save', 'stats', 'context', 'browse', 'delete', 'update', 'export', 'compress', 'maintain', 'optimize', 'fts-check', 'registry', 'import', 'import-jsonl', 'enrich', 'activity', 'adopt', 'unadopt', 'memdir-audit', 'defer', 'help']);
|
|
3
3
|
const INSTALL_COMMANDS = new Set(['install', 'uninstall', 'status', 'doctor', 'cleanup', 'cleanup-hooks', 'self-update', 'release']);
|
|
4
4
|
|
|
5
5
|
const cmd = process.argv[2];
|
package/lib/import-jsonl.mjs
CHANGED
|
@@ -34,12 +34,21 @@ function parseLine(line) {
|
|
|
34
34
|
try { return JSON.parse(line); } catch { return null; }
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
// Distinct mem-internal id derived from the CC session UUID. The schema
|
|
38
|
+
// trigger `sdk_sessions_id_mix_check_*` aborts when memory_session_id ==
|
|
39
|
+
// content_session_id and both look like a CC UUID (length 36 + hyphen
|
|
40
|
+
// pattern); writing the raw UUID into both columns would reproduce the
|
|
41
|
+
// v2.33.1 mix bug. The `import-` prefix makes the origin obvious in audits.
|
|
42
|
+
function memId(sessionId) {
|
|
43
|
+
return `import-${sessionId}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
37
46
|
function ensureSession(db, sessionId, project, ts) {
|
|
38
47
|
db.prepare(`
|
|
39
48
|
INSERT OR IGNORE INTO sdk_sessions
|
|
40
49
|
(content_session_id, memory_session_id, project, started_at, started_at_epoch, status)
|
|
41
50
|
VALUES (?, ?, ?, ?, ?, 'completed')
|
|
42
|
-
`).run(sessionId, sessionId, project, ts, Date.parse(ts) || Date.now());
|
|
51
|
+
`).run(sessionId, memId(sessionId), project, ts, Date.parse(ts) || Date.now());
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
function importPrompt(db, ev, project, seenPrompts) {
|
|
@@ -109,7 +118,7 @@ function importToolPair(db, toolUse, toolResult, project) {
|
|
|
109
118
|
(memory_session_id, project, text, type, title, subtitle, narrative, concepts, facts, files_read, files_modified, importance, created_at, created_at_epoch)
|
|
110
119
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
111
120
|
`).run(
|
|
112
|
-
sessionId, project, safe.text, type, safe.title, safe.subtitle,
|
|
121
|
+
memId(sessionId), project, safe.text, type, safe.title, safe.subtitle,
|
|
113
122
|
safe.narrative, safe.concepts, safe.facts,
|
|
114
123
|
JSON.stringify(filesRead), JSON.stringify(filesModified),
|
|
115
124
|
1, ts, Date.parse(ts) || Date.now(),
|
|
@@ -171,7 +180,9 @@ export async function importJsonl(db, path, { project }) {
|
|
|
171
180
|
const toolName = useEv.name || 'unknown';
|
|
172
181
|
const titlePreview = `${toolName}: ${(useEv.input?.command || useEv.input?.file_path || '').slice(0, 80)}`;
|
|
173
182
|
const ts = useEv.timestamp || new Date().toISOString();
|
|
174
|
-
|
|
183
|
+
// Match the storage convention from importToolPair (memId-prefixed) so
|
|
184
|
+
// the seenObs entries seeded from the DB can be matched on a re-run.
|
|
185
|
+
const crossKey = dedupKey([memId(sessionId), `existing:${titlePreview}:${ts}`]);
|
|
175
186
|
if (seenObs.has(crossKey)) return false;
|
|
176
187
|
|
|
177
188
|
return importToolPair(db, useEv, resultEv, project);
|
|
@@ -183,7 +194,31 @@ export async function importJsonl(db, path, { project }) {
|
|
|
183
194
|
const ev = parseLine(line);
|
|
184
195
|
if (!ev) { skipped++; continue; }
|
|
185
196
|
if (ev.type === 'user') {
|
|
186
|
-
|
|
197
|
+
// Real Claude Code transcripts wrap tool_result inside a user-typed
|
|
198
|
+
// event's message.content array (alongside the rare text part). The
|
|
199
|
+
// top-level {"type":"tool_result"} shape only appears in our test
|
|
200
|
+
// fixtures. Consume any embedded tool_result parts here; only fall
|
|
201
|
+
// through to importPrompt when the event is an actual user prompt.
|
|
202
|
+
const content = ev?.message?.content;
|
|
203
|
+
let consumedAsToolResult = false;
|
|
204
|
+
if (Array.isArray(content)) {
|
|
205
|
+
for (const part of content) {
|
|
206
|
+
if (part?.type === 'tool_result' && part.tool_use_id) {
|
|
207
|
+
consumedAsToolResult = true;
|
|
208
|
+
const useEv = pendingToolUse.get(part.tool_use_id);
|
|
209
|
+
if (useEv) {
|
|
210
|
+
const synth = { content: part.content, tool_use_id: part.tool_use_id, timestamp: ev.timestamp };
|
|
211
|
+
if (tryImportToolPair(useEv, synth)) observations++;
|
|
212
|
+
pendingToolUse.delete(part.tool_use_id);
|
|
213
|
+
} else {
|
|
214
|
+
skipped++;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (!consumedAsToolResult) {
|
|
220
|
+
if (importPrompt(db, ev, project, seenPrompts)) prompts++; else skipped++;
|
|
221
|
+
}
|
|
187
222
|
} else if (ev.type === 'assistant' && Array.isArray(ev.message?.content)) {
|
|
188
223
|
for (const part of ev.message.content) {
|
|
189
224
|
if (part.type === 'tool_use') {
|