code-session-memory 0.4.4 → 0.6.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.
- package/README.md +75 -58
- package/dist/mcp/index.js +23 -2
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server.d.ts +4 -2
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +11 -3
- package/dist/mcp/server.js.map +1 -1
- package/dist/src/cli-sessions.d.ts +6 -7
- package/dist/src/cli-sessions.d.ts.map +1 -1
- package/dist/src/cli-sessions.js +238 -178
- package/dist/src/cli-sessions.js.map +1 -1
- package/dist/src/cli.js +272 -12
- package/dist/src/cli.js.map +1 -1
- package/dist/src/cursor-to-messages.d.ts +64 -0
- package/dist/src/cursor-to-messages.d.ts.map +1 -0
- package/dist/src/cursor-to-messages.js +243 -0
- package/dist/src/cursor-to-messages.js.map +1 -0
- package/dist/src/cursor-transcript-to-messages.d.ts +22 -0
- package/dist/src/cursor-transcript-to-messages.d.ts.map +1 -0
- package/dist/src/cursor-transcript-to-messages.js +79 -0
- package/dist/src/cursor-transcript-to-messages.js.map +1 -0
- package/dist/src/database.d.ts +13 -2
- package/dist/src/database.d.ts.map +1 -1
- package/dist/src/database.js +42 -8
- package/dist/src/database.js.map +1 -1
- package/dist/src/indexer-cli-cursor.d.ts +25 -0
- package/dist/src/indexer-cli-cursor.d.ts.map +1 -0
- package/dist/src/indexer-cli-cursor.js +118 -0
- package/dist/src/indexer-cli-cursor.js.map +1 -0
- package/dist/src/indexer.d.ts.map +1 -1
- package/dist/src/indexer.js +46 -9
- package/dist/src/indexer.js.map +1 -1
- package/dist/src/types.d.ts +6 -1
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/skill/memory.md +7 -2
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Entry point for Cursor session indexing.
|
|
5
|
+
*
|
|
6
|
+
* Called by the Cursor stop hook. Receives JSON on stdin:
|
|
7
|
+
* {
|
|
8
|
+
* conversation_id: string, // the composerId
|
|
9
|
+
* workspace_roots: string[], // project directories
|
|
10
|
+
* transcript_path: string, // path to the JSONL transcript (always complete)
|
|
11
|
+
* model: string,
|
|
12
|
+
* status: "completed" | "aborted" | "error",
|
|
13
|
+
* ...
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* Strategy:
|
|
17
|
+
* - Read messages from transcript_path (JSONL written by Cursor before the
|
|
18
|
+
* hook fires — always complete and race-condition-free).
|
|
19
|
+
* - Read session title from state.vscdb (best-effort, falls back to first
|
|
20
|
+
* user message).
|
|
21
|
+
* - Index new messages into the shared sqlite-vec DB incrementally.
|
|
22
|
+
*
|
|
23
|
+
* No retries needed: the transcript file is the authoritative source.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
const database_1 = require("./database");
|
|
27
|
+
const indexer_1 = require("./indexer");
|
|
28
|
+
const cursor_to_messages_1 = require("./cursor-to-messages");
|
|
29
|
+
const cursor_transcript_to_messages_1 = require("./cursor-transcript-to-messages");
|
|
30
|
+
async function main() {
|
|
31
|
+
// Read JSON payload from stdin
|
|
32
|
+
const chunks = [];
|
|
33
|
+
for await (const chunk of process.stdin) {
|
|
34
|
+
chunks.push(chunk);
|
|
35
|
+
}
|
|
36
|
+
let payload;
|
|
37
|
+
try {
|
|
38
|
+
payload = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
process.stderr.write(`[code-session-memory] Failed to parse stdin: ${err}\n`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
const { conversation_id: composerId, workspace_roots: workspaceRoots, transcript_path: transcriptPath, } = payload;
|
|
45
|
+
if (!composerId) {
|
|
46
|
+
process.stderr.write("[code-session-memory] Missing conversation_id in hook payload\n");
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
if (!transcriptPath) {
|
|
50
|
+
process.stderr.write("[code-session-memory] Missing transcript_path in hook payload — cannot index\n");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// Determine the project directory from workspace_roots
|
|
54
|
+
const projectDir = (workspaceRoots ?? [])
|
|
55
|
+
.map((r) => r.replace(/^file:\/\//, ""))
|
|
56
|
+
.filter(Boolean)[0] ?? "";
|
|
57
|
+
// Read messages from the transcript JSONL.
|
|
58
|
+
// Cursor writes this file synchronously before firing the hook, so it is
|
|
59
|
+
// always complete — no retry needed.
|
|
60
|
+
const messages = (0, cursor_transcript_to_messages_1.cursorTranscriptToMessages)(transcriptPath, composerId);
|
|
61
|
+
if (messages.length === 0) {
|
|
62
|
+
process.stderr.write(`[code-session-memory] No messages in transcript: ${transcriptPath}\n`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const dbPath = (0, database_1.resolveDbPath)();
|
|
66
|
+
const db = (0, database_1.openDatabase)({ dbPath });
|
|
67
|
+
try {
|
|
68
|
+
// Derive session title from SQLite (best-effort — don't fail if unavailable)
|
|
69
|
+
const existingMeta = (0, database_1.getSessionMeta)(db, composerId);
|
|
70
|
+
let title = existingMeta?.session_title ?? "";
|
|
71
|
+
if (!title) {
|
|
72
|
+
try {
|
|
73
|
+
const cursorDb = (0, cursor_to_messages_1.openCursorDb)((0, cursor_to_messages_1.resolveCursorDbPath)());
|
|
74
|
+
try {
|
|
75
|
+
const composer = (0, cursor_to_messages_1.getComposerData)(cursorDb, composerId);
|
|
76
|
+
if (composer) {
|
|
77
|
+
title = (0, cursor_to_messages_1.deriveCursorSessionTitle)(composer, messages);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
cursorDb.close();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// SQLite unavailable — fall back to first user message text
|
|
86
|
+
}
|
|
87
|
+
if (!title) {
|
|
88
|
+
for (const msg of messages) {
|
|
89
|
+
if (msg.info.role === "user") {
|
|
90
|
+
const part = msg.parts.find((p) => p.type === "text");
|
|
91
|
+
if (part && part.type === "text") {
|
|
92
|
+
title = (part.text ?? "").replace(/\s+/g, " ").trim().slice(0, 80);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const session = {
|
|
100
|
+
id: composerId,
|
|
101
|
+
title: title || composerId,
|
|
102
|
+
directory: projectDir,
|
|
103
|
+
};
|
|
104
|
+
await (0, indexer_1.indexNewMessages)(db, session, messages, "cursor");
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
108
|
+
process.stderr.write(`[code-session-memory] Indexing error: ${msg}\n`);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
db.close();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
main().catch((err) => {
|
|
115
|
+
process.stderr.write(`[code-session-memory] Fatal: ${err}\n`);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
});
|
|
118
|
+
//# sourceMappingURL=indexer-cli-cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer-cli-cursor.js","sourceRoot":"","sources":["../../src/indexer-cli-cursor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../../src/indexer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAU3C,MAAM,WAAW,cAAc;IAC7B,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,QAAQ,EACZ,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,WAAW,EAAE,EACvB,MAAM,GAAE,aAA0B,EAClC,OAAO,GAAE,IAAI,CAAC,cAAc,EAAE,cAAc,GAAG,gBAAgB,CAAM,GACpE,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../../src/indexer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAU3C,MAAM,WAAW,cAAc;IAC7B,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,QAAQ,EACZ,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,WAAW,EAAE,EACvB,MAAM,GAAE,aAA0B,EAClC,OAAO,GAAE,IAAI,CAAC,cAAc,EAAE,cAAc,GAAG,gBAAgB,CAAM,GACpE,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA+G/C;AAMD;;;GAGG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,WAAW,EAAE,EACvB,MAAM,GAAE,aAA0B,EAClC,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAU/C;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAmB9B"}
|
package/dist/src/indexer.js
CHANGED
|
@@ -34,14 +34,25 @@ async function indexNewMessages(db, session, messages, source = "opencode", opti
|
|
|
34
34
|
// Load or initialise the session meta record
|
|
35
35
|
const meta = (0, database_1.getSessionMeta)(db, sessionId);
|
|
36
36
|
const lastIndexedId = meta?.last_indexed_message_id ?? null;
|
|
37
|
-
// Filter to only messages after the last indexed one
|
|
37
|
+
// Filter to only messages after the last indexed one.
|
|
38
|
+
// If lastIndexedId is set but not found in the message list, this means the
|
|
39
|
+
// ID format changed (e.g. migrating from SQLite bubble IDs to transcript line
|
|
40
|
+
// IDs). In that case, purge the existing session chunks and re-index from
|
|
41
|
+
// scratch to avoid duplicates.
|
|
38
42
|
let newMessages;
|
|
39
43
|
if (lastIndexedId === null) {
|
|
40
44
|
newMessages = messages;
|
|
41
45
|
}
|
|
42
46
|
else {
|
|
43
47
|
const lastIdx = messages.findIndex((m) => m.info.id === lastIndexedId);
|
|
44
|
-
|
|
48
|
+
if (lastIdx === -1) {
|
|
49
|
+
// ID not found — purge stale chunks and re-index everything
|
|
50
|
+
(0, database_1.deleteSession)(db, sessionId);
|
|
51
|
+
newMessages = messages;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
newMessages = messages.slice(lastIdx + 1);
|
|
55
|
+
}
|
|
45
56
|
}
|
|
46
57
|
if (newMessages.length === 0) {
|
|
47
58
|
return { indexed: 0, skipped: messages.length };
|
|
@@ -50,11 +61,22 @@ async function indexNewMessages(db, session, messages, source = "opencode", opti
|
|
|
50
61
|
apiKey: options.openAiApiKey,
|
|
51
62
|
model: options.embeddingModel,
|
|
52
63
|
});
|
|
53
|
-
|
|
54
|
-
|
|
64
|
+
// Single timestamp for all chunks in this indexing run — represents when
|
|
65
|
+
// the session turn was indexed (within seconds of when it was written).
|
|
66
|
+
const indexedAt = Date.now();
|
|
67
|
+
// The first new message's position within the full session (0-based).
|
|
68
|
+
// Used to assign stable message_order values so chunks sort chronologically
|
|
69
|
+
// regardless of message ID format (works for OpenCode, Claude Code, Cursor).
|
|
70
|
+
const firstNewMessageOrder = messages.length - newMessages.length;
|
|
71
|
+
const perMessage = [];
|
|
72
|
+
const allTexts = [];
|
|
73
|
+
for (let i = 0; i < newMessages.length; i++) {
|
|
74
|
+
const msg = newMessages[i];
|
|
55
75
|
const md = (0, session_to_md_1.messageToMarkdown)(msg);
|
|
56
|
-
if (!md.trim())
|
|
76
|
+
if (!md.trim()) {
|
|
77
|
+
perMessage.push({ chunks: [] });
|
|
57
78
|
continue;
|
|
79
|
+
}
|
|
58
80
|
const msgUrl = `session://${sessionId}#${msg.info.id}`;
|
|
59
81
|
const chunks = (0, chunker_1.chunkMarkdown)(md, {
|
|
60
82
|
sessionId,
|
|
@@ -62,14 +84,26 @@ async function indexNewMessages(db, session, messages, source = "opencode", opti
|
|
|
62
84
|
project,
|
|
63
85
|
baseUrl: msgUrl,
|
|
64
86
|
});
|
|
87
|
+
// Stamp every chunk with the indexing time and message position
|
|
88
|
+
const messageOrder = firstNewMessageOrder + i;
|
|
89
|
+
for (const chunk of chunks) {
|
|
90
|
+
chunk.metadata.created_at = indexedAt;
|
|
91
|
+
chunk.metadata.message_order = messageOrder;
|
|
92
|
+
}
|
|
93
|
+
perMessage.push({ chunks });
|
|
94
|
+
allTexts.push(...chunks.map((c) => c.content));
|
|
95
|
+
}
|
|
96
|
+
// --- Phase 2: embed all chunks in one batch ---
|
|
97
|
+
const allEmbeddings = allTexts.length > 0 ? await embedder.embedBatch(allTexts) : [];
|
|
98
|
+
// --- Phase 3: slice embeddings back per message and insert ---
|
|
99
|
+
let embeddingOffset = 0;
|
|
100
|
+
for (const { chunks } of perMessage) {
|
|
65
101
|
if (chunks.length === 0)
|
|
66
102
|
continue;
|
|
67
|
-
const
|
|
68
|
-
const embeddings = await embedder.embedBatch(texts);
|
|
103
|
+
const embeddings = allEmbeddings.slice(embeddingOffset, embeddingOffset + chunks.length);
|
|
69
104
|
(0, database_1.insertChunks)(db, chunks, embeddings);
|
|
70
|
-
|
|
105
|
+
embeddingOffset += chunks.length;
|
|
71
106
|
}
|
|
72
|
-
void totalChunksIndexed;
|
|
73
107
|
// Update session meta with the last message we processed
|
|
74
108
|
const lastMsg = newMessages[newMessages.length - 1];
|
|
75
109
|
(0, database_1.upsertSessionMeta)(db, {
|
|
@@ -80,6 +114,9 @@ async function indexNewMessages(db, session, messages, source = "opencode", opti
|
|
|
80
114
|
last_indexed_message_id: lastMsg.info.id,
|
|
81
115
|
updated_at: Date.now(),
|
|
82
116
|
});
|
|
117
|
+
// Flush WAL to the main DB file so that a subsequent status check on a
|
|
118
|
+
// separate connection sees the newly indexed data immediately.
|
|
119
|
+
db.pragma("wal_checkpoint(PASSIVE)");
|
|
83
120
|
return { indexed: newMessages.length, skipped: messages.length - newMessages.length };
|
|
84
121
|
}
|
|
85
122
|
// ---------------------------------------------------------------------------
|
package/dist/src/indexer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["../../src/indexer.ts"],"names":[],"mappings":";;AAsCA,
|
|
1
|
+
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["../../src/indexer.ts"],"names":[],"mappings":"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"}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -14,12 +14,16 @@ export interface DocumentChunk {
|
|
|
14
14
|
hash: string;
|
|
15
15
|
chunk_index: number;
|
|
16
16
|
total_chunks: number;
|
|
17
|
+
/** 0-based position of this message within the session (set at index time). Used for correct print ordering. */
|
|
18
|
+
message_order?: number;
|
|
19
|
+
/** Unix ms timestamp set at insert time (Date.now()). Used for date filtering. */
|
|
20
|
+
created_at?: number;
|
|
17
21
|
};
|
|
18
22
|
}
|
|
19
23
|
/**
|
|
20
24
|
* Which tool produced a session.
|
|
21
25
|
*/
|
|
22
|
-
export type SessionSource = "opencode" | "claude-code";
|
|
26
|
+
export type SessionSource = "opencode" | "claude-code" | "cursor";
|
|
23
27
|
/**
|
|
24
28
|
* A row in the sessions_meta table — tracks per-session indexing progress.
|
|
25
29
|
*/
|
|
@@ -92,6 +96,7 @@ export interface QueryResult {
|
|
|
92
96
|
chunk_index?: number;
|
|
93
97
|
total_chunks?: number;
|
|
94
98
|
source?: SessionSource;
|
|
99
|
+
created_at?: number;
|
|
95
100
|
}
|
|
96
101
|
/**
|
|
97
102
|
* Config for the database layer.
|
package/dist/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,iBAAiB,EAAE,MAAM,EAAE,CAAC;QAC5B,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-session-memory",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Automatically index OpenCode
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Automatically index OpenCode, Claude Code, and Cursor sessions into a shared sqlite-vec vector database for semantic search across your AI coding history",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "dist/src/indexer.js",
|
|
7
7
|
"types": "dist/src/indexer.d.ts",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"keywords": [
|
|
27
27
|
"opencode",
|
|
28
28
|
"claude-code",
|
|
29
|
+
"cursor",
|
|
29
30
|
"mcp",
|
|
30
31
|
"sqlite-vec",
|
|
31
32
|
"vector",
|
package/skill/memory.md
CHANGED
|
@@ -15,8 +15,10 @@ Semantically search across all indexed sessions to find past conversations, deci
|
|
|
15
15
|
**Parameters:**
|
|
16
16
|
- `queryText` *(required)*: A natural language description of what you are looking for.
|
|
17
17
|
- `project` *(optional)*: Filter results to a specific project directory path (e.g. `"/Users/me/myproject"`).
|
|
18
|
-
- `source` *(optional)*: Filter by tool — `"opencode"
|
|
18
|
+
- `source` *(optional)*: Filter by tool — `"opencode"`, `"claude-code"`, or `"cursor"`. Omit to search across all.
|
|
19
19
|
- `limit` *(optional, default 5)*: Number of results to return (1–20).
|
|
20
|
+
- `fromDate` *(optional)*: Return only chunks indexed on or after this date. ISO 8601, e.g. `"2026-02-01"` or `"2026-02-20T15:00:00Z"`.
|
|
21
|
+
- `toDate` *(optional)*: Return only chunks indexed on or before this date. ISO 8601, e.g. `"2026-02-20"`. Date-only values are treated as end-of-day UTC.
|
|
20
22
|
|
|
21
23
|
### `get_session_chunks`
|
|
22
24
|
|
|
@@ -49,13 +51,16 @@ query_sessions("dark mode toggle", project="/Users/me/myapp", source="opencode")
|
|
|
49
51
|
# Search only Claude Code sessions
|
|
50
52
|
query_sessions("sqlite migration", source="claude-code")
|
|
51
53
|
|
|
54
|
+
# Search sessions from a specific date range
|
|
55
|
+
query_sessions("authentication middleware", fromDate="2026-02-01", toDate="2026-02-20")
|
|
56
|
+
|
|
52
57
|
# Get more context from a specific result
|
|
53
58
|
get_session_chunks("session://ses_abc123#msg_def456")
|
|
54
59
|
```
|
|
55
60
|
|
|
56
61
|
## Notes
|
|
57
62
|
|
|
58
|
-
- Sessions from
|
|
63
|
+
- Sessions from OpenCode, Claude Code, and Cursor are indexed into the **same** database.
|
|
59
64
|
- Indexing is automatic — no manual action needed.
|
|
60
65
|
- The database lives at `~/.local/share/code-session-memory/sessions.db`.
|
|
61
66
|
- Embeddings use OpenAI `text-embedding-3-large` (3072 dimensions).
|