code-session-memory 0.3.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 (52) hide show
  1. package/README.md +290 -0
  2. package/dist/mcp/index.d.ts +15 -0
  3. package/dist/mcp/index.d.ts.map +1 -0
  4. package/dist/mcp/index.js +140 -0
  5. package/dist/mcp/index.js.map +1 -0
  6. package/dist/mcp/server.d.ts +60 -0
  7. package/dist/mcp/server.d.ts.map +1 -0
  8. package/dist/mcp/server.js +192 -0
  9. package/dist/mcp/server.js.map +1 -0
  10. package/dist/src/chunker.d.ts +18 -0
  11. package/dist/src/chunker.d.ts.map +1 -0
  12. package/dist/src/chunker.js +149 -0
  13. package/dist/src/chunker.js.map +1 -0
  14. package/dist/src/cli.d.ts +11 -0
  15. package/dist/src/cli.d.ts.map +1 -0
  16. package/dist/src/cli.js +514 -0
  17. package/dist/src/cli.js.map +1 -0
  18. package/dist/src/database.d.ts +50 -0
  19. package/dist/src/database.d.ts.map +1 -0
  20. package/dist/src/database.js +238 -0
  21. package/dist/src/database.js.map +1 -0
  22. package/dist/src/embedder.d.ts +19 -0
  23. package/dist/src/embedder.d.ts.map +1 -0
  24. package/dist/src/embedder.js +80 -0
  25. package/dist/src/embedder.js.map +1 -0
  26. package/dist/src/indexer-cli-claude.d.ts +14 -0
  27. package/dist/src/indexer-cli-claude.d.ts.map +1 -0
  28. package/dist/src/indexer-cli-claude.js +67 -0
  29. package/dist/src/indexer-cli-claude.js.map +1 -0
  30. package/dist/src/indexer-cli.d.ts +16 -0
  31. package/dist/src/indexer-cli.d.ts.map +1 -0
  32. package/dist/src/indexer-cli.js +53 -0
  33. package/dist/src/indexer-cli.js.map +1 -0
  34. package/dist/src/indexer.d.ts +44 -0
  35. package/dist/src/indexer.d.ts.map +1 -0
  36. package/dist/src/indexer.js +129 -0
  37. package/dist/src/indexer.js.map +1 -0
  38. package/dist/src/session-to-md.d.ts +15 -0
  39. package/dist/src/session-to-md.d.ts.map +1 -0
  40. package/dist/src/session-to-md.js +148 -0
  41. package/dist/src/session-to-md.js.map +1 -0
  42. package/dist/src/transcript-to-messages.d.ts +32 -0
  43. package/dist/src/transcript-to-messages.d.ts.map +1 -0
  44. package/dist/src/transcript-to-messages.js +230 -0
  45. package/dist/src/transcript-to-messages.js.map +1 -0
  46. package/dist/src/types.d.ts +91 -0
  47. package/dist/src/types.d.ts.map +1 -0
  48. package/dist/src/types.js +3 -0
  49. package/dist/src/types.js.map +1 -0
  50. package/package.json +59 -0
  51. package/plugin/memory.ts +42 -0
  52. package/skill/memory.md +61 -0
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveDbPath = resolveDbPath;
7
+ exports.openDatabase = openDatabase;
8
+ exports.initSchema = initSchema;
9
+ exports.getSessionMeta = getSessionMeta;
10
+ exports.upsertSessionMeta = upsertSessionMeta;
11
+ exports.insertChunks = insertChunks;
12
+ exports.queryByEmbedding = queryByEmbedding;
13
+ exports.getChunksByUrl = getChunksByUrl;
14
+ exports.listSessionUrls = listSessionUrls;
15
+ const path_1 = __importDefault(require("path"));
16
+ const os_1 = __importDefault(require("os"));
17
+ const fs_1 = __importDefault(require("fs"));
18
+ const DEFAULT_EMBEDDING_DIMENSION = 3072;
19
+ // ---------------------------------------------------------------------------
20
+ // Path resolution
21
+ // ---------------------------------------------------------------------------
22
+ /**
23
+ * Resolves the default DB path cross-platform:
24
+ * - Respects OPENCODE_MEMORY_DB_PATH env var
25
+ * - Falls back to ~/.local/share/opencode-memory/sessions.db (works on both
26
+ * macOS and Linux)
27
+ */
28
+ function resolveDbPath(overridePath) {
29
+ if (overridePath) {
30
+ return overridePath.replace(/^~/, os_1.default.homedir());
31
+ }
32
+ const envPath = process.env.OPENCODE_MEMORY_DB_PATH;
33
+ if (envPath) {
34
+ return envPath.replace(/^~/, os_1.default.homedir());
35
+ }
36
+ return path_1.default.join(os_1.default.homedir(), ".local", "share", "code-session-memory", "sessions.db");
37
+ }
38
+ // Lazy-loaded to avoid requiring the native module at import time (useful in tests).
39
+ let _Database = null;
40
+ let _sqliteVec = null;
41
+ function loadDeps() {
42
+ if (!_Database) {
43
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
44
+ _Database = require("better-sqlite3");
45
+ }
46
+ if (!_sqliteVec) {
47
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
48
+ _sqliteVec = require("sqlite-vec");
49
+ }
50
+ return { Database: _Database, sqliteVec: _sqliteVec };
51
+ }
52
+ /**
53
+ * Opens (or creates) a better-sqlite3 database with the sqlite-vec extension
54
+ * loaded. Initialises the schema if needed.
55
+ */
56
+ function openDatabase(config) {
57
+ const { Database, sqliteVec } = loadDeps();
58
+ const { dbPath, embeddingDimension = DEFAULT_EMBEDDING_DIMENSION } = config;
59
+ // Ensure the directory exists
60
+ fs_1.default.mkdirSync(path_1.default.dirname(dbPath), { recursive: true });
61
+ const db = new Database(dbPath);
62
+ sqliteVec.load(db);
63
+ // Performance tuning
64
+ db.pragma("journal_mode = WAL");
65
+ db.pragma("synchronous = NORMAL");
66
+ initSchema(db, embeddingDimension);
67
+ return db;
68
+ }
69
+ /**
70
+ * Creates the schema tables if they don't already exist.
71
+ * Exported so tests can call it with an in-memory DB.
72
+ */
73
+ function initSchema(db, embeddingDimension = DEFAULT_EMBEDDING_DIMENSION) {
74
+ // Virtual table for vector search
75
+ db.exec(`
76
+ CREATE VIRTUAL TABLE IF NOT EXISTS vec_items USING vec0(
77
+ embedding FLOAT[${embeddingDimension}],
78
+ session_id TEXT,
79
+ session_title TEXT,
80
+ project TEXT,
81
+ heading_hierarchy TEXT,
82
+ section TEXT,
83
+ chunk_id TEXT UNIQUE,
84
+ content TEXT,
85
+ url TEXT,
86
+ hash TEXT,
87
+ chunk_index INTEGER,
88
+ total_chunks INTEGER
89
+ );
90
+ `);
91
+ // Metadata table for tracking incremental indexing progress
92
+ db.exec(`
93
+ CREATE TABLE IF NOT EXISTS sessions_meta (
94
+ session_id TEXT PRIMARY KEY,
95
+ session_title TEXT NOT NULL DEFAULT '',
96
+ project TEXT NOT NULL DEFAULT '',
97
+ source TEXT NOT NULL DEFAULT 'opencode',
98
+ last_indexed_message_id TEXT,
99
+ updated_at INTEGER NOT NULL DEFAULT 0
100
+ );
101
+ `);
102
+ // Migrate existing DBs: add source column if missing
103
+ try {
104
+ db.exec(`ALTER TABLE sessions_meta ADD COLUMN source TEXT NOT NULL DEFAULT 'opencode'`);
105
+ }
106
+ catch {
107
+ // Column already exists — ignore
108
+ }
109
+ }
110
+ // ---------------------------------------------------------------------------
111
+ // Session meta CRUD
112
+ // ---------------------------------------------------------------------------
113
+ function getSessionMeta(db, sessionId) {
114
+ const row = db
115
+ .prepare("SELECT * FROM sessions_meta WHERE session_id = ?")
116
+ .get(sessionId);
117
+ return row ?? null;
118
+ }
119
+ function upsertSessionMeta(db, meta) {
120
+ db.prepare(`
121
+ INSERT INTO sessions_meta (session_id, session_title, project, source, last_indexed_message_id, updated_at)
122
+ VALUES (?, ?, ?, ?, ?, ?)
123
+ ON CONFLICT(session_id) DO UPDATE SET
124
+ session_title = excluded.session_title,
125
+ project = excluded.project,
126
+ source = excluded.source,
127
+ last_indexed_message_id = excluded.last_indexed_message_id,
128
+ updated_at = excluded.updated_at
129
+ `).run(meta.session_id, meta.session_title, meta.project, meta.source, meta.last_indexed_message_id, meta.updated_at);
130
+ }
131
+ // ---------------------------------------------------------------------------
132
+ // Vector insertion
133
+ // ---------------------------------------------------------------------------
134
+ /**
135
+ * Inserts a batch of chunks + their embeddings inside a single transaction.
136
+ * Chunks with duplicate chunk_ids are silently skipped (IGNORE conflict).
137
+ */
138
+ function insertChunks(db, chunks, embeddings) {
139
+ if (chunks.length !== embeddings.length) {
140
+ throw new Error(`Mismatch: ${chunks.length} chunks but ${embeddings.length} embeddings`);
141
+ }
142
+ if (chunks.length === 0)
143
+ return;
144
+ const insert = db.prepare(`
145
+ INSERT INTO vec_items (
146
+ embedding, session_id, session_title, project,
147
+ heading_hierarchy, section, chunk_id, content, url, hash,
148
+ chunk_index, total_chunks
149
+ ) VALUES (
150
+ ?, ?, ?, ?,
151
+ ?, ?, ?, ?, ?, ?,
152
+ ?, ?
153
+ )
154
+ `);
155
+ // sqlite-vec does not enforce UNIQUE constraints via INSERT OR IGNORE on
156
+ // virtual tables, so we check for existence first.
157
+ const exists = db.prepare("SELECT 1 FROM vec_items WHERE chunk_id = ? LIMIT 1");
158
+ const insertMany = db.transaction((...args) => {
159
+ const rows = args[0];
160
+ for (const { chunk, embedding } of rows) {
161
+ const { metadata: m } = chunk;
162
+ // Skip if chunk already exists (idempotent indexing)
163
+ if (exists.get(m.chunk_id))
164
+ continue;
165
+ insert.run(new Float32Array(embedding), m.session_id, m.session_title, m.project, JSON.stringify(m.heading_hierarchy), m.section, m.chunk_id, chunk.content, m.url, m.hash, BigInt(m.chunk_index), BigInt(m.total_chunks));
166
+ }
167
+ });
168
+ insertMany(chunks.map((chunk, i) => ({ chunk, embedding: embeddings[i] })));
169
+ }
170
+ // ---------------------------------------------------------------------------
171
+ // Query helpers (used by MCP server)
172
+ // ---------------------------------------------------------------------------
173
+ function queryByEmbedding(db, queryEmbedding, topK = 10, projectFilter, sourceFilter) {
174
+ // sqlite-vec requires the LIMIT (k) constraint to be part of the KNN WHERE
175
+ // clause. We use a CTE to perform the KNN first, then join sessions_meta for
176
+ // the source column and apply optional post-filters.
177
+ let sql = `
178
+ WITH knn AS (
179
+ SELECT
180
+ chunk_id, content, url, section, heading_hierarchy,
181
+ chunk_index, total_chunks, session_id, session_title, project,
182
+ distance
183
+ FROM vec_items
184
+ WHERE embedding MATCH ?
185
+ AND k = ?
186
+ )
187
+ SELECT knn.*, m.source
188
+ FROM knn
189
+ LEFT JOIN sessions_meta m ON knn.session_id = m.session_id
190
+ WHERE 1=1
191
+ `;
192
+ const params = [new Float32Array(queryEmbedding), topK];
193
+ if (projectFilter) {
194
+ sql += " AND knn.project = ?";
195
+ params.push(projectFilter);
196
+ }
197
+ if (sourceFilter) {
198
+ sql += " AND m.source = ?";
199
+ params.push(sourceFilter);
200
+ }
201
+ sql += " ORDER BY distance";
202
+ const rows = db.prepare(sql).all(...params);
203
+ // Strip raw embedding bytes from results
204
+ rows.forEach((r) => {
205
+ if (r && typeof r === "object")
206
+ delete r["embedding"];
207
+ });
208
+ return rows;
209
+ }
210
+ function getChunksByUrl(db, url, startIndex, endIndex) {
211
+ let sql = `
212
+ SELECT chunk_id, content, url, section, heading_hierarchy, chunk_index, total_chunks
213
+ FROM vec_items
214
+ WHERE url = ?
215
+ `;
216
+ const params = [url];
217
+ if (typeof startIndex === "number") {
218
+ sql += " AND chunk_index >= ?";
219
+ params.push(startIndex);
220
+ }
221
+ if (typeof endIndex === "number") {
222
+ sql += " AND chunk_index <= ?";
223
+ params.push(endIndex);
224
+ }
225
+ sql += " ORDER BY chunk_index";
226
+ return db.prepare(sql).all(...params);
227
+ }
228
+ /**
229
+ * Lists all session URLs stored in the DB (for "get_session_chunks" calls that
230
+ * pass a session_id instead of a full URL).
231
+ */
232
+ function listSessionUrls(db, sessionId) {
233
+ const rows = db
234
+ .prepare("SELECT DISTINCT url FROM vec_items WHERE session_id = ? ORDER BY url")
235
+ .all(sessionId);
236
+ return rows.map((r) => r.url);
237
+ }
238
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/database.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { OpenAI } from "openai";
2
+ export interface EmbedderOptions {
3
+ apiKey?: string;
4
+ model?: string;
5
+ /** Override for testing */
6
+ client?: OpenAI;
7
+ }
8
+ /**
9
+ * Creates an embedder function bound to a given OpenAI client/model.
10
+ */
11
+ export declare function createEmbedder(options?: EmbedderOptions): {
12
+ embedText: (text: string) => Promise<number[]>;
13
+ embedBatch: (texts: string[]) => Promise<number[][]>;
14
+ };
15
+ /** Convenience wrapper that uses the default embedder singleton. */
16
+ export declare function embedBatch(texts: string[]): Promise<number[][]>;
17
+ /** Convenience wrapper that uses the default embedder singleton. */
18
+ export declare function embedText(text: string): Promise<number[]>;
19
+ //# sourceMappingURL=embedder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedder.d.ts","sourceRoot":"","sources":["../../src/embedder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAiBhC,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,eAAoB;sBAS3B,MAAM,KAAG,OAAO,CAAC,MAAM,EAAE,CAAC;wBAmBxB,MAAM,EAAE,KAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;EA4BhE;AAeD,oEAAoE;AACpE,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAErE;AAED,oEAAoE;AACpE,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAE/D"}
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createEmbedder = createEmbedder;
4
+ exports.embedBatch = embedBatch;
5
+ exports.embedText = embedText;
6
+ const openai_1 = require("openai");
7
+ // ---------------------------------------------------------------------------
8
+ // Constants (matching doc2vec limits)
9
+ // ---------------------------------------------------------------------------
10
+ const MAX_EMBEDDING_TOKENS = 8191; // OpenAI text-embedding-3-large limit
11
+ const CHARS_PER_TOKEN = 4; // Conservative BPE estimate
12
+ const MAX_EMBEDDING_CHARS = MAX_EMBEDDING_TOKENS * CHARS_PER_TOKEN; // 32 764
13
+ // Batch size: OpenAI allows up to 2048 inputs per request, but keep it
14
+ // conservative to stay well within rate-limits.
15
+ const BATCH_SIZE = 64;
16
+ /**
17
+ * Creates an embedder function bound to a given OpenAI client/model.
18
+ */
19
+ function createEmbedder(options = {}) {
20
+ const model = options.model ?? process.env.OPENAI_MODEL ?? "text-embedding-3-large";
21
+ const client = options.client ??
22
+ new openai_1.OpenAI({ apiKey: options.apiKey ?? process.env.OPENAI_API_KEY });
23
+ /**
24
+ * Embeds a single text string. Truncates if necessary.
25
+ */
26
+ async function embedText(text) {
27
+ const truncated = text.length > MAX_EMBEDDING_CHARS ? text.slice(0, MAX_EMBEDDING_CHARS) : text;
28
+ const response = await client.embeddings.create({
29
+ model,
30
+ input: truncated,
31
+ });
32
+ const embedding = response.data?.[0]?.embedding;
33
+ if (!embedding) {
34
+ throw new Error("No embedding returned from OpenAI API");
35
+ }
36
+ return embedding;
37
+ }
38
+ /**
39
+ * Embeds a batch of texts, splitting into sub-batches to avoid API limits.
40
+ */
41
+ async function embedBatch(texts) {
42
+ if (texts.length === 0)
43
+ return [];
44
+ const results = [];
45
+ for (let i = 0; i < texts.length; i += BATCH_SIZE) {
46
+ const batch = texts.slice(i, i + BATCH_SIZE).map((t) => t.length > MAX_EMBEDDING_CHARS ? t.slice(0, MAX_EMBEDDING_CHARS) : t);
47
+ const response = await client.embeddings.create({
48
+ model,
49
+ input: batch,
50
+ });
51
+ // OpenAI returns embeddings in the same order as inputs
52
+ const sorted = response.data
53
+ .slice()
54
+ .sort((a, b) => a.index - b.index)
55
+ .map((d) => d.embedding);
56
+ results.push(...sorted);
57
+ }
58
+ return results;
59
+ }
60
+ return { embedText, embedBatch };
61
+ }
62
+ // ---------------------------------------------------------------------------
63
+ // Default singleton (lazy-initialised)
64
+ // ---------------------------------------------------------------------------
65
+ let _defaultEmbedder = null;
66
+ function getDefaultEmbedder() {
67
+ if (!_defaultEmbedder) {
68
+ _defaultEmbedder = createEmbedder();
69
+ }
70
+ return _defaultEmbedder;
71
+ }
72
+ /** Convenience wrapper that uses the default embedder singleton. */
73
+ async function embedBatch(texts) {
74
+ return getDefaultEmbedder().embedBatch(texts);
75
+ }
76
+ /** Convenience wrapper that uses the default embedder singleton. */
77
+ async function embedText(text) {
78
+ return getDefaultEmbedder().embedText(text);
79
+ }
80
+ //# sourceMappingURL=embedder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedder.js","sourceRoot":"","sources":["../../src/embedder.ts"],"names":[],"mappings":";;AA2BA,wCAwDC;AAgBD,gCAEC;AAGD,8BAEC;AA1GD,mCAAgC;AAEhC,8EAA8E;AAC9E,sCAAsC;AACtC,8EAA8E;AAC9E,MAAM,oBAAoB,GAAG,IAAI,CAAC,CAAC,sCAAsC;AACzE,MAAM,eAAe,GAAG,CAAC,CAAC,CAAC,4BAA4B;AACvD,MAAM,mBAAmB,GAAG,oBAAoB,GAAG,eAAe,CAAC,CAAC,SAAS;AAE7E,uEAAuE;AACvE,gDAAgD;AAChD,MAAM,UAAU,GAAG,EAAE,CAAC;AAatB;;GAEG;AACH,SAAgB,cAAc,CAAC,UAA2B,EAAE;IAC1D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,wBAAwB,CAAC;IACpF,MAAM,MAAM,GACV,OAAO,CAAC,MAAM;QACd,IAAI,eAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC;IAEvE;;OAEG;IACH,KAAK,UAAU,SAAS,CAAC,IAAY;QACnC,MAAM,SAAS,GACb,IAAI,CAAC,MAAM,GAAG,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAEhF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;YAC9C,KAAK;YACL,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC;QAChD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;OAEG;IACH,KAAK,UAAU,UAAU,CAAC,KAAe;QACvC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAElC,MAAM,OAAO,GAAe,EAAE,CAAC;QAE/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC;YAClD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACrD,CAAC,CAAC,MAAM,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,CACrE,CAAC;YAEF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;gBAC9C,KAAK;gBACL,KAAK,EAAE,KAAK;aACb,CAAC,CAAC;YAEH,wDAAwD;YACxD,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI;iBACzB,KAAK,EAAE;iBACP,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;iBACjC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAE3B,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;QAC1B,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;AACnC,CAAC;AAED,8EAA8E;AAC9E,uCAAuC;AACvC,8EAA8E;AAE9E,IAAI,gBAAgB,GAA6C,IAAI,CAAC;AAEtE,SAAS,kBAAkB;IACzB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,gBAAgB,GAAG,cAAc,EAAE,CAAC;IACtC,CAAC;IACD,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED,oEAAoE;AAC7D,KAAK,UAAU,UAAU,CAAC,KAAe;IAC9C,OAAO,kBAAkB,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AAChD,CAAC;AAED,oEAAoE;AAC7D,KAAK,UAAU,SAAS,CAAC,IAAY;IAC1C,OAAO,kBAAkB,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Entry point for Claude Code session indexing.
4
+ *
5
+ * Called by the Claude Code Stop hook. Receives JSON on stdin:
6
+ * { session_id, transcript_path, cwd, ... }
7
+ *
8
+ * Reads the transcript file, converts to FullMessage[], and indexes
9
+ * new messages into the shared sqlite-vec DB.
10
+ *
11
+ * Runs as a Node.js subprocess (not Bun) so native addons load correctly.
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=indexer-cli-claude.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexer-cli-claude.d.ts","sourceRoot":"","sources":["../../src/indexer-cli-claude.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG"}
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * Entry point for Claude Code session indexing.
5
+ *
6
+ * Called by the Claude Code Stop hook. Receives JSON on stdin:
7
+ * { session_id, transcript_path, cwd, ... }
8
+ *
9
+ * Reads the transcript file, converts to FullMessage[], and indexes
10
+ * new messages into the shared sqlite-vec DB.
11
+ *
12
+ * Runs as a Node.js subprocess (not Bun) so native addons load correctly.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const database_1 = require("./database");
16
+ const indexer_1 = require("./indexer");
17
+ const transcript_to_messages_1 = require("./transcript-to-messages");
18
+ async function main() {
19
+ // Read JSON payload from stdin
20
+ const chunks = [];
21
+ for await (const chunk of process.stdin) {
22
+ chunks.push(chunk);
23
+ }
24
+ let payload;
25
+ try {
26
+ payload = JSON.parse(Buffer.concat(chunks).toString("utf8"));
27
+ }
28
+ catch (err) {
29
+ process.stderr.write(`[code-session-memory] Failed to parse stdin: ${err}\n`);
30
+ process.exit(1);
31
+ }
32
+ const { session_id: sessionId, transcript_path: transcriptPath, cwd } = payload;
33
+ if (!sessionId || !transcriptPath) {
34
+ process.stderr.write("[code-session-memory] Missing session_id or transcript_path in stdin\n");
35
+ process.exit(1);
36
+ }
37
+ const dbPath = (0, database_1.resolveDbPath)();
38
+ const db = (0, database_1.openDatabase)({ dbPath });
39
+ try {
40
+ // Parse the transcript
41
+ const messages = (0, transcript_to_messages_1.parseTranscript)(transcriptPath);
42
+ if (messages.length === 0) {
43
+ return;
44
+ }
45
+ // Build a session title from the first user message
46
+ const existingMeta = (0, database_1.getSessionMeta)(db, sessionId);
47
+ const title = existingMeta?.session_title || (0, transcript_to_messages_1.deriveSessionTitle)(messages);
48
+ const session = {
49
+ id: sessionId,
50
+ title,
51
+ directory: cwd ?? "",
52
+ };
53
+ await (0, indexer_1.indexNewMessages)(db, session, messages, "claude-code");
54
+ }
55
+ catch (err) {
56
+ const msg = err instanceof Error ? err.message : String(err);
57
+ process.stderr.write(`[code-session-memory] Indexing error: ${msg}\n`);
58
+ }
59
+ finally {
60
+ db.close();
61
+ }
62
+ }
63
+ main().catch((err) => {
64
+ process.stderr.write(`[code-session-memory] Fatal: ${err}\n`);
65
+ process.exit(1);
66
+ });
67
+ //# sourceMappingURL=indexer-cli-claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexer-cli-claude.js","sourceRoot":"","sources":["../../src/indexer-cli-claude.ts"],"names":[],"mappings":";;AACA;;;;;;;;;;GAUG;;AAEH,yCAAyE;AACzE,uCAA6C;AAC7C,qEAA+E;AAE/E,KAAK,UAAU,IAAI;IACjB,+BAA+B;IAC/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,OAAwE,CAAC;IAC7E,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gDAAgD,GAAG,IAAI,CAAC,CAAC;QAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IAEhF,IAAI,CAAC,SAAS,IAAI,CAAC,cAAc,EAAE,CAAC;QAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wEAAwE,CAAC,CAAC;QAC/F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,wBAAa,GAAE,CAAC;IAC/B,MAAM,EAAE,GAAG,IAAA,uBAAY,EAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAEpC,IAAI,CAAC;QACH,uBAAuB;QACvB,MAAM,QAAQ,GAAG,IAAA,wCAAe,EAAC,cAAc,CAAC,CAAC;QACjD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,oDAAoD;QACpD,MAAM,YAAY,GAAG,IAAA,yBAAc,EAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QACnD,MAAM,KAAK,GAAG,YAAY,EAAE,aAAa,IAAI,IAAA,2CAAkB,EAAC,QAAQ,CAAC,CAAC;QAE1E,MAAM,OAAO,GAAG;YACd,EAAE,EAAE,SAAS;YACb,KAAK;YACL,SAAS,EAAE,GAAG,IAAI,EAAE;SACrB,CAAC;QAEF,MAAM,IAAA,0BAAgB,EAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;IAC/D,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yCAAyC,GAAG,IAAI,CAAC,CAAC;IACzE,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,GAAG,IAAI,CAAC,CAAC;IAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * indexer-cli — subprocess entry point called by the OpenCode plugin.
4
+ *
5
+ * Runs under Node.js (not Bun) so that native modules (better-sqlite3,
6
+ * sqlite-vec) load correctly.
7
+ *
8
+ * Usage (called by plugin/memory.ts via the Bun $ shell):
9
+ * node /path/to/dist/src/indexer-cli.js <sessionId> <serverUrl>
10
+ *
11
+ * serverUrl is the URL of the already-running OpenCode server, passed in
12
+ * by the plugin from the `serverUrl` context it receives at startup.
13
+ * Uses plain fetch() to call the REST API — no ESM/CJS SDK dependency.
14
+ */
15
+ export {};
16
+ //# sourceMappingURL=indexer-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexer-cli.d.ts","sourceRoot":"","sources":["../../src/indexer-cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG"}
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * indexer-cli — subprocess entry point called by the OpenCode plugin.
5
+ *
6
+ * Runs under Node.js (not Bun) so that native modules (better-sqlite3,
7
+ * sqlite-vec) load correctly.
8
+ *
9
+ * Usage (called by plugin/memory.ts via the Bun $ shell):
10
+ * node /path/to/dist/src/indexer-cli.js <sessionId> <serverUrl>
11
+ *
12
+ * serverUrl is the URL of the already-running OpenCode server, passed in
13
+ * by the plugin from the `serverUrl` context it receives at startup.
14
+ * Uses plain fetch() to call the REST API — no ESM/CJS SDK dependency.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ const database_1 = require("./database");
18
+ const indexer_1 = require("./indexer");
19
+ async function fetchJson(url) {
20
+ const res = await fetch(url);
21
+ if (!res.ok) {
22
+ throw new Error(`HTTP ${res.status} fetching ${url}: ${await res.text()}`);
23
+ }
24
+ return res.json();
25
+ }
26
+ async function main() {
27
+ const sessionId = process.argv[2];
28
+ const serverUrl = process.argv[3];
29
+ if (!sessionId || !serverUrl) {
30
+ process.stderr.write("Usage: indexer-cli <sessionId> <serverUrl>\n");
31
+ process.exit(1);
32
+ }
33
+ // Normalize: strip trailing slash
34
+ const base = serverUrl.replace(/\/$/, "");
35
+ const [session, messages] = await Promise.all([
36
+ fetchJson(`${base}/session/${sessionId}`),
37
+ fetchJson(`${base}/session/${sessionId}/message`),
38
+ ]);
39
+ const dbPath = (0, database_1.resolveDbPath)();
40
+ const db = (0, database_1.openDatabase)({ dbPath });
41
+ try {
42
+ await (0, indexer_1.indexNewMessages)(db, { id: session.id, title: session.title, directory: session.directory }, messages, "opencode");
43
+ }
44
+ finally {
45
+ db.close();
46
+ }
47
+ // No output — the plugin runs this silently via Bun's $.quiet()
48
+ }
49
+ main().catch((err) => {
50
+ process.stderr.write(`[code-session-memory] indexer-cli error: ${err instanceof Error ? err.message : String(err)}\n`);
51
+ process.exit(1);
52
+ });
53
+ //# sourceMappingURL=indexer-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexer-cli.js","sourceRoot":"","sources":["../../src/indexer-cli.ts"],"names":[],"mappings":";;AACA;;;;;;;;;;;;GAYG;;AAEH,yCAAyD;AACzD,uCAA6C;AAG7C,KAAK,UAAU,SAAS,CAAI,GAAW;IACrC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,MAAM,aAAa,GAAG,KAAK,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAgB,CAAC;AAClC,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAElC,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,EAAE,CAAC;QAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;QACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,kCAAkC;IAClC,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAE1C,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC5C,SAAS,CACP,GAAG,IAAI,YAAY,SAAS,EAAE,CAC/B;QACD,SAAS,CAAgB,GAAG,IAAI,YAAY,SAAS,UAAU,CAAC;KACjE,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,IAAA,wBAAa,GAAE,CAAC;IAC/B,MAAM,EAAE,GAAG,IAAA,uBAAY,EAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,IAAA,0BAAgB,EACpB,EAAE,EACF,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,EACtE,QAAQ,EACR,UAAU,CACX,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;IAED,gEAAgE;AAClE,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,4CAA4C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CACjG,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,44 @@
1
+ import type { SessionInfo, FullMessage, SessionSource } from "./types";
2
+ import type { Database } from "./database";
3
+ export interface IndexerOptions {
4
+ /** Override the DB path (useful for testing). Falls back to resolveDbPath(). */
5
+ dbPath?: string;
6
+ /** Override the OpenAI API key. Falls back to OPENAI_API_KEY env var. */
7
+ openAiApiKey?: string;
8
+ /** Override the embedding model. Falls back to text-embedding-3-large. */
9
+ embeddingModel?: string;
10
+ }
11
+ /**
12
+ * Incrementally indexes new messages from a session into the vector DB.
13
+ *
14
+ * Only messages that have not been previously indexed are processed:
15
+ * - We store the last indexed message ID in sessions_meta.
16
+ * - On each call, we filter to messages that come AFTER that ID.
17
+ * - Each new message is converted to markdown, chunked, embedded and stored.
18
+ *
19
+ * @param db Already-open database connection (caller manages lifecycle)
20
+ * @param session Session metadata (id, title, directory)
21
+ * @param messages All messages in the session
22
+ * @param source Which tool produced the session ("opencode" | "claude-code")
23
+ * @param options Optional overrides for API key / model
24
+ */
25
+ export declare function indexNewMessages(db: Database, session: SessionInfo, messages: FullMessage[], source?: SessionSource, options?: Pick<IndexerOptions, "openAiApiKey" | "embeddingModel">): Promise<{
26
+ indexed: number;
27
+ skipped: number;
28
+ }>;
29
+ /**
30
+ * Convenience wrapper that opens its own DB connection.
31
+ * Used by the OpenCode indexer-cli and by tests.
32
+ */
33
+ export declare function indexNewMessagesWithOptions(session: SessionInfo, messages: FullMessage[], source?: SessionSource, options?: IndexerOptions): Promise<{
34
+ indexed: number;
35
+ skipped: number;
36
+ }>;
37
+ /**
38
+ * Re-indexes all messages in a session from scratch.
39
+ * Useful for repairing a corrupted or stale index.
40
+ */
41
+ export declare function reindexSession(session: SessionInfo, messages: FullMessage[], options?: IndexerOptions): Promise<{
42
+ indexed: number;
43
+ }>;
44
+ //# sourceMappingURL=indexer.d.ts.map
@@ -0,0 +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,CAoE/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"}