claude-launchpad 0.6.0 → 0.7.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.
@@ -0,0 +1,390 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/commands/memory/storage/memory-repo.ts
4
+ import { randomUUID } from "crypto";
5
+ function safeParseTags(raw) {
6
+ try {
7
+ const parsed = JSON.parse(raw);
8
+ return Array.isArray(parsed) ? parsed.filter((t) => typeof t === "string") : [];
9
+ } catch {
10
+ return [];
11
+ }
12
+ }
13
+ function rowToMemory(row) {
14
+ return {
15
+ id: row.id,
16
+ type: row.type,
17
+ title: row.title,
18
+ content: row.content,
19
+ context: row.context,
20
+ source: row.source,
21
+ project: row.project,
22
+ tags: safeParseTags(row.tags),
23
+ importance: row.importance,
24
+ createdAt: row.created_at,
25
+ updatedAt: row.updated_at,
26
+ accessCount: row.access_count,
27
+ lastAccessed: row.last_accessed,
28
+ injectionCount: row.injection_count
29
+ };
30
+ }
31
+ var MemoryRepo = class {
32
+ #stmts;
33
+ db;
34
+ constructor(db) {
35
+ this.db = db;
36
+ this.#stmts = {
37
+ insert: db.prepare(`
38
+ INSERT INTO memories (id, type, title, content, context, source, project, tags, importance, created_at, updated_at, embedding)
39
+ VALUES (@id, @type, @title, @content, @context, @source, @project, @tags, @importance, @createdAt, @updatedAt, @embedding)
40
+ `),
41
+ getById: db.prepare("SELECT * FROM memories WHERE id = ?"),
42
+ getAll: db.prepare("SELECT * FROM memories ORDER BY created_at DESC"),
43
+ getAllByProject: db.prepare("SELECT * FROM memories WHERE project = ? OR project IS NULL ORDER BY created_at DESC"),
44
+ getByType: db.prepare("SELECT * FROM memories WHERE type = ? ORDER BY created_at DESC"),
45
+ getByTypeAndProject: db.prepare("SELECT * FROM memories WHERE type = ? AND (project = ? OR project IS NULL) ORDER BY created_at DESC"),
46
+ getRecent: db.prepare("SELECT * FROM memories ORDER BY created_at DESC LIMIT ?"),
47
+ getRecentByProject: db.prepare("SELECT * FROM memories WHERE project = ? OR project IS NULL ORDER BY created_at DESC LIMIT ?"),
48
+ getRecentByTypeAndProject: db.prepare("SELECT * FROM memories WHERE type = ? AND (project = ? OR project IS NULL) ORDER BY created_at DESC LIMIT ?"),
49
+ update: db.prepare(`
50
+ UPDATE memories
51
+ SET title = @title, content = @content, context = @context, tags = @tags,
52
+ importance = @importance, updated_at = @updatedAt, embedding = @embedding
53
+ WHERE id = @id
54
+ `),
55
+ updateImportance: db.prepare("UPDATE memories SET importance = ?, updated_at = ? WHERE id = ?"),
56
+ updateImportanceOnly: db.prepare("UPDATE memories SET importance = ? WHERE id = ?"),
57
+ incrementAccess: db.prepare(`
58
+ UPDATE memories SET access_count = access_count + 1, last_accessed = ? WHERE id = ?
59
+ `),
60
+ incrementInjection: db.prepare(`
61
+ UPDATE memories SET injection_count = injection_count + 1 WHERE id = ?
62
+ `),
63
+ softDelete: db.prepare("UPDATE memories SET importance = 0, updated_at = ? WHERE id = ?"),
64
+ hardDelete: db.prepare("DELETE FROM memories WHERE id = ?"),
65
+ deleteByType: db.prepare("DELETE FROM memories WHERE type = ?"),
66
+ count: db.prepare("SELECT COUNT(*) as count FROM memories"),
67
+ countByProject: db.prepare("SELECT COUNT(*) as count FROM memories WHERE project = ?"),
68
+ countByType: db.prepare("SELECT type, COUNT(*) as count FROM memories GROUP BY type"),
69
+ dateRange: db.prepare("SELECT MIN(created_at) as oldest, MAX(created_at) as newest FROM memories"),
70
+ topInjected: db.prepare(`
71
+ SELECT id, title, injection_count FROM memories
72
+ WHERE injection_count > 0 ORDER BY injection_count DESC LIMIT ?
73
+ `)
74
+ };
75
+ }
76
+ create(input, _embedding = null) {
77
+ const now = (/* @__PURE__ */ new Date()).toISOString();
78
+ const id = randomUUID();
79
+ const params = {
80
+ id,
81
+ type: input.type,
82
+ title: input.title ?? null,
83
+ content: input.content,
84
+ context: input.context ?? null,
85
+ source: input.source,
86
+ project: input.project ?? null,
87
+ tags: JSON.stringify(input.tags),
88
+ importance: input.importance,
89
+ createdAt: now,
90
+ updatedAt: now,
91
+ embedding: null
92
+ };
93
+ this.#stmts.insert.run(params);
94
+ const row = {
95
+ id,
96
+ type: input.type,
97
+ title: input.title ?? null,
98
+ content: input.content,
99
+ context: input.context ?? null,
100
+ source: input.source,
101
+ project: input.project ?? null,
102
+ tags: JSON.stringify(input.tags),
103
+ importance: input.importance,
104
+ created_at: now,
105
+ updated_at: now,
106
+ access_count: 0,
107
+ last_accessed: null,
108
+ injection_count: 0,
109
+ embedding: null
110
+ };
111
+ return rowToMemory(row);
112
+ }
113
+ getById(id) {
114
+ const row = this.#stmts.getById.get(id);
115
+ return row ? rowToMemory(row) : void 0;
116
+ }
117
+ getAll(project) {
118
+ if (project) {
119
+ const rows2 = this.#stmts.getAllByProject.all(project);
120
+ return rows2.map(rowToMemory);
121
+ }
122
+ const rows = this.#stmts.getAll.all();
123
+ return rows.map(rowToMemory);
124
+ }
125
+ getRecent(limit, project, type) {
126
+ if (type && project) {
127
+ const rows2 = this.#stmts.getRecentByTypeAndProject.all(type, project, limit);
128
+ return rows2.map(rowToMemory);
129
+ }
130
+ if (project) {
131
+ const rows2 = this.#stmts.getRecentByProject.all(project, limit);
132
+ return rows2.map(rowToMemory);
133
+ }
134
+ const rows = this.#stmts.getRecent.all(limit);
135
+ return rows.map(rowToMemory);
136
+ }
137
+ getByType(type, project) {
138
+ if (project) {
139
+ const rows2 = this.#stmts.getByTypeAndProject.all(type, project);
140
+ return rows2.map(rowToMemory);
141
+ }
142
+ const rows = this.#stmts.getByType.all(type);
143
+ return rows.map(rowToMemory);
144
+ }
145
+ updateContent(id, updates) {
146
+ const existing = this.getById(id);
147
+ if (!existing) return false;
148
+ const now = (/* @__PURE__ */ new Date()).toISOString();
149
+ const params = {
150
+ id,
151
+ title: updates.title !== void 0 ? updates.title : existing.title,
152
+ content: updates.content ?? existing.content,
153
+ context: updates.context !== void 0 ? updates.context : existing.context,
154
+ tags: JSON.stringify(updates.tags ?? existing.tags),
155
+ importance: updates.importance ?? existing.importance,
156
+ updatedAt: now,
157
+ embedding: null
158
+ };
159
+ this.#stmts.update.run(params);
160
+ return true;
161
+ }
162
+ updateImportance(id, importance) {
163
+ const now = (/* @__PURE__ */ new Date()).toISOString();
164
+ const result = this.#stmts.updateImportance.run(importance, now, id);
165
+ return result.changes > 0;
166
+ }
167
+ /** Update importance without touching updated_at - used by decay to avoid resetting the clock. */
168
+ updateImportanceOnly(id, importance) {
169
+ const result = this.#stmts.updateImportanceOnly.run(importance, id);
170
+ return result.changes > 0;
171
+ }
172
+ incrementAccess(id) {
173
+ this.#stmts.incrementAccess.run((/* @__PURE__ */ new Date()).toISOString(), id);
174
+ }
175
+ incrementInjection(id) {
176
+ this.#stmts.incrementInjection.run(id);
177
+ }
178
+ softDelete(id) {
179
+ const result = this.#stmts.softDelete.run((/* @__PURE__ */ new Date()).toISOString(), id);
180
+ return result.changes > 0;
181
+ }
182
+ hardDelete(id) {
183
+ const result = this.#stmts.hardDelete.run(id);
184
+ return result.changes > 0;
185
+ }
186
+ deleteByType(type) {
187
+ const result = this.#stmts.deleteByType.run(type);
188
+ return result.changes;
189
+ }
190
+ count(project) {
191
+ if (project) {
192
+ const row2 = this.#stmts.countByProject.get(project);
193
+ return row2.count;
194
+ }
195
+ const row = this.#stmts.count.get();
196
+ return row.count;
197
+ }
198
+ countByType() {
199
+ const rows = this.#stmts.countByType.all();
200
+ return Object.fromEntries(rows.map((r) => [r.type, r.count]));
201
+ }
202
+ dateRange() {
203
+ const row = this.#stmts.dateRange.get();
204
+ return { oldest: row.oldest, newest: row.newest };
205
+ }
206
+ topInjected(limit = 5) {
207
+ const rows = this.#stmts.topInjected.all(limit);
208
+ return rows.map((r) => ({ id: r.id, title: r.title, injectionCount: r.injection_count }));
209
+ }
210
+ };
211
+
212
+ // src/commands/memory/storage/relation-repo.ts
213
+ function rowToRelation(row) {
214
+ return {
215
+ sourceId: row.source_id,
216
+ targetId: row.target_id,
217
+ relationType: row.relation_type,
218
+ createdAt: row.created_at
219
+ };
220
+ }
221
+ var RelationRepo = class {
222
+ #stmts;
223
+ constructor(db) {
224
+ this.#stmts = {
225
+ insert: db.prepare(`
226
+ INSERT OR IGNORE INTO relations (source_id, target_id, relation_type)
227
+ VALUES (?, ?, ?)
228
+ `),
229
+ getBySource: db.prepare("SELECT * FROM relations WHERE source_id = ?"),
230
+ getByTarget: db.prepare("SELECT * FROM relations WHERE target_id = ?"),
231
+ getByMemory: db.prepare(`
232
+ SELECT * FROM relations WHERE source_id = ? OR target_id = ?
233
+ `),
234
+ delete: db.prepare(`
235
+ DELETE FROM relations WHERE source_id = ? AND target_id = ? AND relation_type = ?
236
+ `),
237
+ countByMemory: db.prepare(`
238
+ SELECT COUNT(*) as count FROM relations WHERE source_id = ? OR target_id = ?
239
+ `),
240
+ count: db.prepare("SELECT COUNT(*) as count FROM relations")
241
+ };
242
+ }
243
+ create(sourceId, targetId, relationType) {
244
+ const result = this.#stmts.insert.run(sourceId, targetId, relationType);
245
+ return result.changes > 0;
246
+ }
247
+ getBySource(sourceId) {
248
+ const rows = this.#stmts.getBySource.all(sourceId);
249
+ return rows.map(rowToRelation);
250
+ }
251
+ getByTarget(targetId) {
252
+ const rows = this.#stmts.getByTarget.all(targetId);
253
+ return rows.map(rowToRelation);
254
+ }
255
+ getByMemory(memoryId) {
256
+ const rows = this.#stmts.getByMemory.all(memoryId, memoryId);
257
+ return rows.map(rowToRelation);
258
+ }
259
+ delete(sourceId, targetId, relationType) {
260
+ const result = this.#stmts.delete.run(sourceId, targetId, relationType);
261
+ return result.changes > 0;
262
+ }
263
+ countByMemory(memoryId) {
264
+ const row = this.#stmts.countByMemory.get(memoryId, memoryId);
265
+ return row.count;
266
+ }
267
+ count() {
268
+ const row = this.#stmts.count.get();
269
+ return row.count;
270
+ }
271
+ };
272
+
273
+ // src/commands/memory/storage/search-repo.ts
274
+ var SearchRepo = class {
275
+ #stmts;
276
+ constructor(db) {
277
+ this.#stmts = {
278
+ ftsSearch: db.prepare(`
279
+ SELECT
280
+ m.rowid,
281
+ m.id as memory_id,
282
+ bm25(memories_fts, 5.0, 1.0, 2.0) as rank
283
+ FROM memories_fts f
284
+ JOIN memories m ON m.rowid = f.rowid
285
+ WHERE memories_fts MATCH @query
286
+ ORDER BY rank
287
+ LIMIT @limit
288
+ `),
289
+ ftsSearchByProject: db.prepare(`
290
+ SELECT
291
+ m.rowid,
292
+ m.id as memory_id,
293
+ bm25(memories_fts, 5.0, 1.0, 2.0) as rank
294
+ FROM memories_fts f
295
+ JOIN memories m ON m.rowid = f.rowid
296
+ WHERE memories_fts MATCH @query
297
+ AND (m.project = @project OR m.project IS NULL)
298
+ ORDER BY rank
299
+ LIMIT @limit
300
+ `),
301
+ ftsSearchFiltered: db.prepare(`
302
+ SELECT
303
+ m.rowid,
304
+ m.id as memory_id,
305
+ bm25(memories_fts, 5.0, 1.0, 2.0) as rank
306
+ FROM memories_fts f
307
+ JOIN memories m ON m.rowid = f.rowid
308
+ WHERE memories_fts MATCH @query
309
+ AND m.type = @type
310
+ AND m.importance >= @minImportance
311
+ ORDER BY rank
312
+ LIMIT @limit
313
+ `),
314
+ ftsSearchFilteredByProject: db.prepare(`
315
+ SELECT
316
+ m.rowid,
317
+ m.id as memory_id,
318
+ bm25(memories_fts, 5.0, 1.0, 2.0) as rank
319
+ FROM memories_fts f
320
+ JOIN memories m ON m.rowid = f.rowid
321
+ WHERE memories_fts MATCH @query
322
+ AND m.type = @type
323
+ AND m.importance >= @minImportance
324
+ AND (m.project = @project OR m.project IS NULL)
325
+ ORDER BY rank
326
+ LIMIT @limit
327
+ `)
328
+ };
329
+ }
330
+ /**
331
+ * Full-text search using BM25 ranking.
332
+ * Returns matches sorted by relevance (most relevant first).
333
+ */
334
+ searchFts(options) {
335
+ const ftsQuery = toFtsQuery(options.query);
336
+ if (!ftsQuery) return [];
337
+ try {
338
+ const hasType = !!options.type;
339
+ const hasProject = !!options.project;
340
+ let rows;
341
+ if (hasType && hasProject) {
342
+ rows = this.#stmts.ftsSearchFilteredByProject.all({
343
+ query: ftsQuery,
344
+ limit: options.limit,
345
+ type: options.type,
346
+ minImportance: options.minImportance ?? 0,
347
+ project: options.project
348
+ });
349
+ } else if (hasType) {
350
+ rows = this.#stmts.ftsSearchFiltered.all({
351
+ query: ftsQuery,
352
+ limit: options.limit,
353
+ type: options.type,
354
+ minImportance: options.minImportance ?? 0
355
+ });
356
+ } else if (hasProject) {
357
+ rows = this.#stmts.ftsSearchByProject.all({
358
+ query: ftsQuery,
359
+ limit: options.limit,
360
+ project: options.project
361
+ });
362
+ } else {
363
+ rows = this.#stmts.ftsSearch.all({
364
+ query: ftsQuery,
365
+ limit: options.limit
366
+ });
367
+ }
368
+ return rows.map((r) => ({
369
+ rowid: r.rowid,
370
+ memoryId: r.memory_id,
371
+ rank: r.rank
372
+ }));
373
+ } catch (err) {
374
+ console.error("[agentic-memory] FTS5 search error:", err instanceof Error ? err.message : err);
375
+ return [];
376
+ }
377
+ }
378
+ };
379
+ function toFtsQuery(input) {
380
+ const words = input.replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 0);
381
+ if (words.length === 0) return null;
382
+ return words.map((w) => `"${w}"`).join(" OR ");
383
+ }
384
+
385
+ export {
386
+ MemoryRepo,
387
+ RelationRepo,
388
+ SearchRepo
389
+ };
390
+ //# sourceMappingURL=chunk-TALTTAMW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/memory/storage/memory-repo.ts","../src/commands/memory/storage/relation-repo.ts","../src/commands/memory/storage/search-repo.ts"],"sourcesContent":["import type Database from 'better-sqlite3';\nimport type { Memory, MemoryType, MemorySource, StoreInput } from '../types.js';\nimport { randomUUID } from 'node:crypto';\n\nfunction safeParseTags(raw: string): string[] {\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed.filter(t => typeof t === 'string') : [];\n } catch {\n return [];\n }\n}\n\n// ── Row shape from SQLite ─────────────────────────────────────\n\ninterface MemoryRow {\n id: string;\n type: string;\n title: string | null;\n content: string;\n context: string | null;\n source: string | null;\n project: string | null;\n tags: string;\n importance: number;\n created_at: string;\n updated_at: string;\n access_count: number;\n last_accessed: string | null;\n injection_count: number;\n embedding: Buffer | null;\n}\n\nfunction rowToMemory(row: MemoryRow): Memory {\n return {\n id: row.id,\n type: row.type as MemoryType,\n title: row.title,\n content: row.content,\n context: row.context,\n source: row.source as MemorySource | null,\n project: row.project,\n tags: safeParseTags(row.tags),\n importance: row.importance,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n accessCount: row.access_count,\n lastAccessed: row.last_accessed,\n injectionCount: row.injection_count,\n };\n}\n\n// ── Repository ────────────────────────────────────────────────\n\nexport class MemoryRepo {\n readonly #stmts;\n readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n this.#stmts = {\n insert: db.prepare(`\n INSERT INTO memories (id, type, title, content, context, source, project, tags, importance, created_at, updated_at, embedding)\n VALUES (@id, @type, @title, @content, @context, @source, @project, @tags, @importance, @createdAt, @updatedAt, @embedding)\n `),\n getById: db.prepare('SELECT * FROM memories WHERE id = ?'),\n getAll: db.prepare('SELECT * FROM memories ORDER BY created_at DESC'),\n getAllByProject: db.prepare('SELECT * FROM memories WHERE project = ? OR project IS NULL ORDER BY created_at DESC'),\n getByType: db.prepare('SELECT * FROM memories WHERE type = ? ORDER BY created_at DESC'),\n getByTypeAndProject: db.prepare('SELECT * FROM memories WHERE type = ? AND (project = ? OR project IS NULL) ORDER BY created_at DESC'),\n getRecent: db.prepare('SELECT * FROM memories ORDER BY created_at DESC LIMIT ?'),\n getRecentByProject: db.prepare('SELECT * FROM memories WHERE project = ? OR project IS NULL ORDER BY created_at DESC LIMIT ?'),\n getRecentByTypeAndProject: db.prepare('SELECT * FROM memories WHERE type = ? AND (project = ? OR project IS NULL) ORDER BY created_at DESC LIMIT ?'),\n update: db.prepare(`\n UPDATE memories\n SET title = @title, content = @content, context = @context, tags = @tags,\n importance = @importance, updated_at = @updatedAt, embedding = @embedding\n WHERE id = @id\n `),\n updateImportance: db.prepare('UPDATE memories SET importance = ?, updated_at = ? WHERE id = ?'),\n updateImportanceOnly: db.prepare('UPDATE memories SET importance = ? WHERE id = ?'),\n incrementAccess: db.prepare(`\n UPDATE memories SET access_count = access_count + 1, last_accessed = ? WHERE id = ?\n `),\n incrementInjection: db.prepare(`\n UPDATE memories SET injection_count = injection_count + 1 WHERE id = ?\n `),\n softDelete: db.prepare('UPDATE memories SET importance = 0, updated_at = ? WHERE id = ?'),\n hardDelete: db.prepare('DELETE FROM memories WHERE id = ?'),\n deleteByType: db.prepare('DELETE FROM memories WHERE type = ?'),\n count: db.prepare('SELECT COUNT(*) as count FROM memories'),\n countByProject: db.prepare('SELECT COUNT(*) as count FROM memories WHERE project = ?'),\n countByType: db.prepare('SELECT type, COUNT(*) as count FROM memories GROUP BY type'),\n dateRange: db.prepare('SELECT MIN(created_at) as oldest, MAX(created_at) as newest FROM memories'),\n topInjected: db.prepare(`\n SELECT id, title, injection_count FROM memories\n WHERE injection_count > 0 ORDER BY injection_count DESC LIMIT ?\n `),\n };\n }\n\n create(input: StoreInput, _embedding: Buffer | null = null): Memory {\n const now = new Date().toISOString();\n const id = randomUUID();\n\n const params = {\n id,\n type: input.type,\n title: input.title ?? null,\n content: input.content,\n context: input.context ?? null,\n source: input.source,\n project: input.project ?? null,\n tags: JSON.stringify(input.tags),\n importance: input.importance,\n createdAt: now,\n updatedAt: now,\n embedding: null,\n };\n\n this.#stmts.insert.run(params);\n\n const row: MemoryRow = {\n id,\n type: input.type,\n title: input.title ?? null,\n content: input.content,\n context: input.context ?? null,\n source: input.source,\n project: input.project ?? null,\n tags: JSON.stringify(input.tags),\n importance: input.importance,\n created_at: now,\n updated_at: now,\n access_count: 0,\n last_accessed: null,\n injection_count: 0,\n embedding: null,\n };\n\n return rowToMemory(row);\n }\n\n getById(id: string): Memory | undefined {\n const row = this.#stmts.getById.get(id) as MemoryRow | undefined;\n return row ? rowToMemory(row) : undefined;\n }\n\n getAll(project?: string): readonly Memory[] {\n if (project) {\n const rows = this.#stmts.getAllByProject.all(project) as MemoryRow[];\n return rows.map(rowToMemory);\n }\n const rows = this.#stmts.getAll.all() as MemoryRow[];\n return rows.map(rowToMemory);\n }\n\n getRecent(limit: number, project?: string, type?: MemoryType): readonly Memory[] {\n if (type && project) {\n const rows = this.#stmts.getRecentByTypeAndProject.all(type, project, limit) as MemoryRow[];\n return rows.map(rowToMemory);\n }\n if (project) {\n const rows = this.#stmts.getRecentByProject.all(project, limit) as MemoryRow[];\n return rows.map(rowToMemory);\n }\n const rows = this.#stmts.getRecent.all(limit) as MemoryRow[];\n return rows.map(rowToMemory);\n }\n\n getByType(type: MemoryType, project?: string): readonly Memory[] {\n if (project) {\n const rows = this.#stmts.getByTypeAndProject.all(type, project) as MemoryRow[];\n return rows.map(rowToMemory);\n }\n const rows = this.#stmts.getByType.all(type) as MemoryRow[];\n return rows.map(rowToMemory);\n }\n\n updateContent(id: string, updates: {\n readonly title?: string | null;\n readonly content?: string;\n readonly context?: string | null;\n readonly tags?: readonly string[];\n readonly importance?: number;\n }): boolean {\n const existing = this.getById(id);\n if (!existing) return false;\n\n const now = new Date().toISOString();\n\n const params = {\n id,\n title: updates.title !== undefined ? updates.title : existing.title,\n content: updates.content ?? existing.content,\n context: updates.context !== undefined ? updates.context : existing.context,\n tags: JSON.stringify(updates.tags ?? existing.tags),\n importance: updates.importance ?? existing.importance,\n updatedAt: now,\n embedding: null,\n };\n\n this.#stmts.update.run(params);\n return true;\n }\n\n updateImportance(id: string, importance: number): boolean {\n const now = new Date().toISOString();\n const result = this.#stmts.updateImportance.run(importance, now, id);\n return result.changes > 0;\n }\n\n /** Update importance without touching updated_at - used by decay to avoid resetting the clock. */\n updateImportanceOnly(id: string, importance: number): boolean {\n const result = this.#stmts.updateImportanceOnly.run(importance, id);\n return result.changes > 0;\n }\n\n incrementAccess(id: string): void {\n this.#stmts.incrementAccess.run(new Date().toISOString(), id);\n }\n\n incrementInjection(id: string): void {\n this.#stmts.incrementInjection.run(id);\n }\n\n softDelete(id: string): boolean {\n const result = this.#stmts.softDelete.run(new Date().toISOString(), id);\n return result.changes > 0;\n }\n\n hardDelete(id: string): boolean {\n const result = this.#stmts.hardDelete.run(id);\n return result.changes > 0;\n }\n\n deleteByType(type: MemoryType): number {\n const result = this.#stmts.deleteByType.run(type);\n return result.changes;\n }\n\n count(project?: string): number {\n if (project) {\n const row = this.#stmts.countByProject.get(project) as { count: number };\n return row.count;\n }\n const row = this.#stmts.count.get() as { count: number };\n return row.count;\n }\n\n countByType(): Record<string, number> {\n const rows = this.#stmts.countByType.all() as { type: string; count: number }[];\n return Object.fromEntries(rows.map(r => [r.type, r.count]));\n }\n\n dateRange(): { oldest: string | null; newest: string | null } {\n const row = this.#stmts.dateRange.get() as { oldest: string | null; newest: string | null };\n return { oldest: row.oldest, newest: row.newest };\n }\n\n topInjected(limit: number = 5): readonly { id: string; title: string | null; injectionCount: number }[] {\n const rows = this.#stmts.topInjected.all(limit) as { id: string; title: string | null; injection_count: number }[];\n return rows.map(r => ({ id: r.id, title: r.title, injectionCount: r.injection_count }));\n }\n}\n","import type Database from 'better-sqlite3';\nimport type { Relation, RelationType } from '../types.js';\n\ninterface RelationRow {\n source_id: string;\n target_id: string;\n relation_type: string;\n created_at: string;\n}\n\nfunction rowToRelation(row: RelationRow): Relation {\n return {\n sourceId: row.source_id,\n targetId: row.target_id,\n relationType: row.relation_type as RelationType,\n createdAt: row.created_at,\n };\n}\n\nexport class RelationRepo {\n readonly #stmts;\n\n constructor(db: Database.Database) {\n this.#stmts = {\n insert: db.prepare(`\n INSERT OR IGNORE INTO relations (source_id, target_id, relation_type)\n VALUES (?, ?, ?)\n `),\n getBySource: db.prepare('SELECT * FROM relations WHERE source_id = ?'),\n getByTarget: db.prepare('SELECT * FROM relations WHERE target_id = ?'),\n getByMemory: db.prepare(`\n SELECT * FROM relations WHERE source_id = ? OR target_id = ?\n `),\n delete: db.prepare(`\n DELETE FROM relations WHERE source_id = ? AND target_id = ? AND relation_type = ?\n `),\n countByMemory: db.prepare(`\n SELECT COUNT(*) as count FROM relations WHERE source_id = ? OR target_id = ?\n `),\n count: db.prepare('SELECT COUNT(*) as count FROM relations'),\n };\n }\n\n create(sourceId: string, targetId: string, relationType: RelationType): boolean {\n const result = this.#stmts.insert.run(sourceId, targetId, relationType);\n return result.changes > 0;\n }\n\n getBySource(sourceId: string): readonly Relation[] {\n const rows = this.#stmts.getBySource.all(sourceId) as RelationRow[];\n return rows.map(rowToRelation);\n }\n\n getByTarget(targetId: string): readonly Relation[] {\n const rows = this.#stmts.getByTarget.all(targetId) as RelationRow[];\n return rows.map(rowToRelation);\n }\n\n getByMemory(memoryId: string): readonly Relation[] {\n const rows = this.#stmts.getByMemory.all(memoryId, memoryId) as RelationRow[];\n return rows.map(rowToRelation);\n }\n\n delete(sourceId: string, targetId: string, relationType: RelationType): boolean {\n const result = this.#stmts.delete.run(sourceId, targetId, relationType);\n return result.changes > 0;\n }\n\n countByMemory(memoryId: string): number {\n const row = this.#stmts.countByMemory.get(memoryId, memoryId) as { count: number };\n return row.count;\n }\n\n count(): number {\n const row = this.#stmts.count.get() as { count: number };\n return row.count;\n }\n}\n","import type Database from 'better-sqlite3';\nimport type { FtsMatch, MemoryType } from '../types.js';\n\n// ── FTS5 Search ───────────────────────────────────────────────\n\nexport interface FtsSearchOptions {\n readonly query: string;\n readonly limit: number;\n readonly type?: MemoryType;\n readonly minImportance?: number;\n readonly project?: string;\n}\n\n\nexport class SearchRepo {\n readonly #stmts;\n\n constructor(db: Database.Database) {\n // FTS5 search with BM25 ranking (weights: title=5.0, content=1.0, tags=2.0)\n this.#stmts = {\n ftsSearch: db.prepare(`\n SELECT\n m.rowid,\n m.id as memory_id,\n bm25(memories_fts, 5.0, 1.0, 2.0) as rank\n FROM memories_fts f\n JOIN memories m ON m.rowid = f.rowid\n WHERE memories_fts MATCH @query\n ORDER BY rank\n LIMIT @limit\n `),\n ftsSearchByProject: db.prepare(`\n SELECT\n m.rowid,\n m.id as memory_id,\n bm25(memories_fts, 5.0, 1.0, 2.0) as rank\n FROM memories_fts f\n JOIN memories m ON m.rowid = f.rowid\n WHERE memories_fts MATCH @query\n AND (m.project = @project OR m.project IS NULL)\n ORDER BY rank\n LIMIT @limit\n `),\n ftsSearchFiltered: db.prepare(`\n SELECT\n m.rowid,\n m.id as memory_id,\n bm25(memories_fts, 5.0, 1.0, 2.0) as rank\n FROM memories_fts f\n JOIN memories m ON m.rowid = f.rowid\n WHERE memories_fts MATCH @query\n AND m.type = @type\n AND m.importance >= @minImportance\n ORDER BY rank\n LIMIT @limit\n `),\n ftsSearchFilteredByProject: db.prepare(`\n SELECT\n m.rowid,\n m.id as memory_id,\n bm25(memories_fts, 5.0, 1.0, 2.0) as rank\n FROM memories_fts f\n JOIN memories m ON m.rowid = f.rowid\n WHERE memories_fts MATCH @query\n AND m.type = @type\n AND m.importance >= @minImportance\n AND (m.project = @project OR m.project IS NULL)\n ORDER BY rank\n LIMIT @limit\n `),\n };\n }\n\n /**\n * Full-text search using BM25 ranking.\n * Returns matches sorted by relevance (most relevant first).\n */\n searchFts(options: FtsSearchOptions): readonly FtsMatch[] {\n const ftsQuery = toFtsQuery(options.query);\n if (!ftsQuery) return [];\n\n try {\n const hasType = !!options.type;\n const hasProject = !!options.project;\n\n let rows: FtsRow[];\n if (hasType && hasProject) {\n rows = this.#stmts.ftsSearchFilteredByProject.all({\n query: ftsQuery, limit: options.limit,\n type: options.type, minImportance: options.minImportance ?? 0,\n project: options.project,\n }) as FtsRow[];\n } else if (hasType) {\n rows = this.#stmts.ftsSearchFiltered.all({\n query: ftsQuery, limit: options.limit,\n type: options.type, minImportance: options.minImportance ?? 0,\n }) as FtsRow[];\n } else if (hasProject) {\n rows = this.#stmts.ftsSearchByProject.all({\n query: ftsQuery, limit: options.limit,\n project: options.project,\n }) as FtsRow[];\n } else {\n rows = this.#stmts.ftsSearch.all({\n query: ftsQuery, limit: options.limit,\n }) as FtsRow[];\n }\n\n return rows.map(r => ({\n rowid: r.rowid,\n memoryId: r.memory_id,\n rank: r.rank,\n }));\n } catch (err) {\n // FTS5 MATCH throws on invalid query syntax - degrade gracefully\n console.error('[agentic-memory] FTS5 search error:', err instanceof Error ? err.message : err);\n return [];\n }\n }\n\n}\n\n// ── Internal helpers ──────────────────────────────────────────\n\ninterface FtsRow {\n rowid: number;\n memory_id: string;\n rank: number;\n}\n\n/**\n * Convert a natural language query to FTS5 query syntax.\n * Wraps each word in quotes to avoid syntax errors from special characters.\n */\nfunction toFtsQuery(input: string): string | null {\n const words = input\n .replace(/[^\\w\\s]/g, ' ') // strip punctuation\n .split(/\\s+/)\n .filter(w => w.length > 0);\n\n if (words.length === 0) return null;\n\n // Use OR between words for broader recall\n return words.map(w => `\"${w}\"`).join(' OR ');\n}\n"],"mappings":"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","names":["rows","row"]}