claude-launchpad 0.16.1 → 1.0.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/README.md +14 -5
- package/dist/{chunk-JQDMBE7W.js → chunk-3UJYOWGF.js} +18 -1
- package/dist/chunk-3UJYOWGF.js.map +1 -0
- package/dist/{chunk-S4WIADYE.js → chunk-4JNFXVVC.js} +49 -49
- package/dist/chunk-4JNFXVVC.js.map +1 -0
- package/dist/{chunk-SVWFSAYP.js → chunk-AUV2JTXX.js} +3 -3
- package/dist/{chunk-IZFQTQ4C.js → chunk-PGDSAUP4.js} +2 -2
- package/dist/{chunk-24VLPHJU.js → chunk-V4NXT4KB.js} +43 -16
- package/dist/chunk-V4NXT4KB.js.map +1 -0
- package/dist/{chunk-NLZGJHE5.js → chunk-ZXJK7CHB.js} +31 -4
- package/dist/chunk-ZXJK7CHB.js.map +1 -0
- package/dist/cli.js +49 -16
- package/dist/cli.js.map +1 -1
- package/dist/commands/memory/server.js +20 -11
- package/dist/commands/memory/server.js.map +1 -1
- package/dist/{context-53PKQOMI.js → context-Q5ZQBY7O.js} +6 -6
- package/dist/{install-PFTFTNIF.js → install-LS7DTMIE.js} +59 -55
- package/dist/install-LS7DTMIE.js.map +1 -0
- package/dist/{pull-4QTS57DQ.js → pull-4NRD4GQ4.js} +13 -9
- package/dist/pull-4NRD4GQ4.js.map +1 -0
- package/dist/{push-JOCEW3VG.js → push-BHYEETGP.js} +9 -9
- package/dist/{require-deps-SUGLVBM2.js → require-deps-3GIE6TAG.js} +3 -3
- package/dist/{stats-7WFCVXBX.js → stats-NQ5NRUZC.js} +7 -7
- package/dist/sync-clean-QWEQVAYO.js +53 -0
- package/dist/sync-clean-QWEQVAYO.js.map +1 -0
- package/dist/sync-status-ZMXMEBGC.js +70 -0
- package/dist/sync-status-ZMXMEBGC.js.map +1 -0
- package/dist/{tui-DKWRY5PT.js → tui-YL5NWME5.js} +5 -5
- package/package.json +1 -1
- package/dist/chunk-24VLPHJU.js.map +0 -1
- package/dist/chunk-JQDMBE7W.js.map +0 -1
- package/dist/chunk-NLZGJHE5.js.map +0 -1
- package/dist/chunk-S4WIADYE.js.map +0 -1
- package/dist/install-PFTFTNIF.js.map +0 -1
- package/dist/pull-4QTS57DQ.js.map +0 -1
- /package/dist/{chunk-SVWFSAYP.js.map → chunk-AUV2JTXX.js.map} +0 -0
- /package/dist/{chunk-IZFQTQ4C.js.map → chunk-PGDSAUP4.js.map} +0 -0
- /package/dist/{context-53PKQOMI.js.map → context-Q5ZQBY7O.js.map} +0 -0
- /package/dist/{push-JOCEW3VG.js.map → push-BHYEETGP.js.map} +0 -0
- /package/dist/{require-deps-SUGLVBM2.js.map → require-deps-3GIE6TAG.js.map} +0 -0
- /package/dist/{stats-7WFCVXBX.js.map → stats-NQ5NRUZC.js.map} +0 -0
- /package/dist/{tui-DKWRY5PT.js.map → tui-YL5NWME5.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/commands/memory/storage/memory-repo.ts
|
|
4
|
-
import { randomUUID } from "crypto";
|
|
4
|
+
import { randomUUID, createHash } from "crypto";
|
|
5
5
|
function safeParseTags(raw) {
|
|
6
6
|
try {
|
|
7
7
|
const parsed = JSON.parse(raw);
|
|
@@ -35,8 +35,8 @@ var MemoryRepo = class {
|
|
|
35
35
|
this.db = db;
|
|
36
36
|
this.#stmts = {
|
|
37
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)
|
|
38
|
+
INSERT OR IGNORE INTO memories (id, type, title, content, context, source, project, tags, importance, created_at, updated_at, embedding, content_hash)
|
|
39
|
+
VALUES (@id, @type, @title, @content, @context, @source, @project, @tags, @importance, @createdAt, @updatedAt, @embedding, @contentHash)
|
|
40
40
|
`),
|
|
41
41
|
getById: db.prepare("SELECT * FROM memories WHERE id = ?"),
|
|
42
42
|
getAll: db.prepare("SELECT * FROM memories ORDER BY created_at DESC"),
|
|
@@ -49,7 +49,8 @@ var MemoryRepo = class {
|
|
|
49
49
|
update: db.prepare(`
|
|
50
50
|
UPDATE memories
|
|
51
51
|
SET title = @title, content = @content, context = @context, tags = @tags,
|
|
52
|
-
importance = @importance, updated_at = @updatedAt, embedding = @embedding
|
|
52
|
+
importance = @importance, updated_at = @updatedAt, embedding = @embedding,
|
|
53
|
+
content_hash = @contentHash
|
|
53
54
|
WHERE id = @id
|
|
54
55
|
`),
|
|
55
56
|
updateImportance: db.prepare("UPDATE memories SET importance = ?, updated_at = ? WHERE id = ?"),
|
|
@@ -67,17 +68,27 @@ var MemoryRepo = class {
|
|
|
67
68
|
count: db.prepare("SELECT COUNT(*) as count FROM memories"),
|
|
68
69
|
countByProject: db.prepare("SELECT COUNT(*) as count FROM memories WHERE project = ?"),
|
|
69
70
|
countByType: db.prepare("SELECT type, COUNT(*) as count FROM memories GROUP BY type"),
|
|
71
|
+
projectCounts: db.prepare("SELECT COALESCE(project, '_global') as project, COUNT(*) as count FROM memories GROUP BY project"),
|
|
70
72
|
dateRange: db.prepare("SELECT MIN(created_at) as oldest, MAX(created_at) as newest FROM memories"),
|
|
71
73
|
topInjected: db.prepare(`
|
|
72
74
|
SELECT id, title, injection_count FROM memories
|
|
73
75
|
WHERE injection_count > 0 ORDER BY injection_count DESC LIMIT ?
|
|
74
76
|
`),
|
|
75
|
-
|
|
76
|
-
INSERT OR
|
|
77
|
+
insertSync: db.prepare(`
|
|
78
|
+
INSERT OR IGNORE INTO memories
|
|
77
79
|
(id, type, title, content, context, source, project, tags, importance,
|
|
78
|
-
access_count, injection_count, created_at, updated_at, last_accessed, embedding)
|
|
80
|
+
access_count, injection_count, created_at, updated_at, last_accessed, embedding, content_hash)
|
|
79
81
|
VALUES (@id, @type, @title, @content, @context, @source, @project, @tags, @importance,
|
|
80
|
-
@accessCount, @injectionCount, @createdAt, @updatedAt, @lastAccessed, NULL)
|
|
82
|
+
@accessCount, @injectionCount, @createdAt, @updatedAt, @lastAccessed, NULL, @contentHash)
|
|
83
|
+
`),
|
|
84
|
+
updateSync: db.prepare(`
|
|
85
|
+
UPDATE memories SET
|
|
86
|
+
title = @title, content = @content, context = @context,
|
|
87
|
+
source = @source, project = @project, tags = @tags,
|
|
88
|
+
importance = @importance, access_count = @accessCount,
|
|
89
|
+
injection_count = @injectionCount, updated_at = @updatedAt,
|
|
90
|
+
last_accessed = @lastAccessed, content_hash = @contentHash
|
|
91
|
+
WHERE id = @id
|
|
81
92
|
`),
|
|
82
93
|
getAllStrictProject: db.prepare(
|
|
83
94
|
"SELECT * FROM memories WHERE project = ? ORDER BY created_at DESC"
|
|
@@ -87,6 +98,7 @@ var MemoryRepo = class {
|
|
|
87
98
|
create(input, _embedding = null) {
|
|
88
99
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
89
100
|
const id = randomUUID();
|
|
101
|
+
const contentHash = createHash("sha256").update(input.content).digest("hex");
|
|
90
102
|
const params = {
|
|
91
103
|
id,
|
|
92
104
|
type: input.type,
|
|
@@ -99,9 +111,11 @@ var MemoryRepo = class {
|
|
|
99
111
|
importance: input.importance,
|
|
100
112
|
createdAt: now,
|
|
101
113
|
updatedAt: now,
|
|
102
|
-
embedding: null
|
|
114
|
+
embedding: null,
|
|
115
|
+
contentHash
|
|
103
116
|
};
|
|
104
|
-
this.#stmts.insert.run(params);
|
|
117
|
+
const result = this.#stmts.insert.run(params);
|
|
118
|
+
if (result.changes === 0) return null;
|
|
105
119
|
const row = {
|
|
106
120
|
id,
|
|
107
121
|
type: input.type,
|
|
@@ -157,15 +171,17 @@ var MemoryRepo = class {
|
|
|
157
171
|
const existing = this.getById(id);
|
|
158
172
|
if (!existing) return false;
|
|
159
173
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
174
|
+
const finalContent = updates.content ?? existing.content;
|
|
160
175
|
const params = {
|
|
161
176
|
id,
|
|
162
177
|
title: updates.title !== void 0 ? updates.title : existing.title,
|
|
163
|
-
content:
|
|
178
|
+
content: finalContent,
|
|
164
179
|
context: updates.context !== void 0 ? updates.context : existing.context,
|
|
165
180
|
tags: JSON.stringify(updates.tags ?? existing.tags),
|
|
166
181
|
importance: updates.importance ?? existing.importance,
|
|
167
182
|
updatedAt: now,
|
|
168
|
-
embedding: null
|
|
183
|
+
embedding: null,
|
|
184
|
+
contentHash: createHash("sha256").update(finalContent).digest("hex")
|
|
169
185
|
};
|
|
170
186
|
this.#stmts.update.run(params);
|
|
171
187
|
return true;
|
|
@@ -214,6 +230,10 @@ var MemoryRepo = class {
|
|
|
214
230
|
const rows = this.#stmts.countByType.all();
|
|
215
231
|
return Object.fromEntries(rows.map((r) => [r.type, r.count]));
|
|
216
232
|
}
|
|
233
|
+
projectCounts() {
|
|
234
|
+
const rows = this.#stmts.projectCounts.all();
|
|
235
|
+
return new Map(rows.map((r) => [r.project, r.count]));
|
|
236
|
+
}
|
|
217
237
|
dateRange() {
|
|
218
238
|
const row = this.#stmts.dateRange.get();
|
|
219
239
|
return { oldest: row.oldest, newest: row.newest };
|
|
@@ -223,7 +243,7 @@ var MemoryRepo = class {
|
|
|
223
243
|
return rows.map((r) => ({ id: r.id, title: r.title, injectionCount: r.injection_count }));
|
|
224
244
|
}
|
|
225
245
|
upsertFromSync(row) {
|
|
226
|
-
|
|
246
|
+
const params = {
|
|
227
247
|
id: row.id,
|
|
228
248
|
type: row.type,
|
|
229
249
|
title: row.title,
|
|
@@ -237,8 +257,15 @@ var MemoryRepo = class {
|
|
|
237
257
|
injectionCount: row.injection_count,
|
|
238
258
|
createdAt: row.created_at,
|
|
239
259
|
updatedAt: row.updated_at,
|
|
240
|
-
lastAccessed: row.last_accessed
|
|
241
|
-
|
|
260
|
+
lastAccessed: row.last_accessed,
|
|
261
|
+
contentHash: createHash("sha256").update(row.content).digest("hex")
|
|
262
|
+
};
|
|
263
|
+
const existing = this.getById(row.id);
|
|
264
|
+
if (existing) {
|
|
265
|
+
this.#stmts.updateSync.run(params);
|
|
266
|
+
} else {
|
|
267
|
+
this.#stmts.insertSync.run(params);
|
|
268
|
+
}
|
|
242
269
|
}
|
|
243
270
|
getAllForSync(project) {
|
|
244
271
|
if (project) {
|
|
@@ -459,4 +486,4 @@ export {
|
|
|
459
486
|
RelationRepo,
|
|
460
487
|
SearchRepo
|
|
461
488
|
};
|
|
462
|
-
//# sourceMappingURL=chunk-
|
|
489
|
+
//# sourceMappingURL=chunk-V4NXT4KB.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, SyncMemoryRow } from '../types.js';\nimport { randomUUID, createHash } 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 OR IGNORE INTO memories (id, type, title, content, context, source, project, tags, importance, created_at, updated_at, embedding, content_hash)\n VALUES (@id, @type, @title, @content, @context, @source, @project, @tags, @importance, @createdAt, @updatedAt, @embedding, @contentHash)\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 content_hash = @contentHash\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 deleteByProject: db.prepare('DELETE FROM memories WHERE project = ?'),\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 projectCounts: db.prepare('SELECT COALESCE(project, \\'_global\\') as project, COUNT(*) as count FROM memories GROUP BY project'),\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 insertSync: db.prepare(`\n INSERT OR IGNORE INTO memories\n (id, type, title, content, context, source, project, tags, importance,\n access_count, injection_count, created_at, updated_at, last_accessed, embedding, content_hash)\n VALUES (@id, @type, @title, @content, @context, @source, @project, @tags, @importance,\n @accessCount, @injectionCount, @createdAt, @updatedAt, @lastAccessed, NULL, @contentHash)\n `),\n updateSync: db.prepare(`\n UPDATE memories SET\n title = @title, content = @content, context = @context,\n source = @source, project = @project, tags = @tags,\n importance = @importance, access_count = @accessCount,\n injection_count = @injectionCount, updated_at = @updatedAt,\n last_accessed = @lastAccessed, content_hash = @contentHash\n WHERE id = @id\n `),\n getAllStrictProject: db.prepare(\n 'SELECT * FROM memories WHERE project = ? ORDER BY created_at DESC'\n ),\n };\n }\n\n create(input: StoreInput, _embedding: Buffer | null = null): Memory | null {\n const now = new Date().toISOString();\n const id = randomUUID();\n const contentHash = createHash('sha256').update(input.content).digest('hex');\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 contentHash,\n };\n\n const result = this.#stmts.insert.run(params);\n if (result.changes === 0) return null;\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 finalContent = updates.content ?? existing.content;\n const params = {\n id,\n title: updates.title !== undefined ? updates.title : existing.title,\n content: finalContent,\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 contentHash: createHash('sha256').update(finalContent).digest('hex'),\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 deleteByProject(project: string): number {\n const result = this.#stmts.deleteByProject.run(project);\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 projectCounts(): ReadonlyMap<string, number> {\n const rows = this.#stmts.projectCounts.all() as { project: string; count: number }[];\n return new Map(rows.map(r => [r.project, 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 upsertFromSync(row: SyncMemoryRow): void {\n const params = {\n id: row.id,\n type: row.type,\n title: row.title,\n content: row.content,\n context: row.context,\n source: row.source,\n project: row.project,\n tags: JSON.stringify(row.tags),\n importance: row.importance,\n accessCount: row.access_count,\n injectionCount: row.injection_count,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n lastAccessed: row.last_accessed,\n contentHash: createHash('sha256').update(row.content).digest('hex'),\n };\n\n const existing = this.getById(row.id);\n if (existing) {\n this.#stmts.updateSync.run(params);\n } else {\n this.#stmts.insertSync.run(params);\n }\n }\n\n getAllForSync(project?: string): readonly Memory[] {\n if (project) {\n const rows = this.#stmts.getAllStrictProject.all(project) as MemoryRow[];\n return rows.map(rowToMemory);\n }\n return this.getAll();\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 getAll: db.prepare('SELECT * FROM relations'),\n deleteOrphaned: db.prepare(`\n DELETE FROM relations\n WHERE source_id NOT IN (SELECT id FROM memories)\n OR target_id NOT IN (SELECT id FROM memories)\n `),\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 getAll(): readonly Relation[] {\n const rows = this.#stmts.getAll.all() as RelationRow[];\n return rows.map(rowToRelation);\n }\n\n deleteOrphaned(): number {\n const result = this.#stmts.deleteOrphaned.run();\n return result.changes;\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// Synonym expansion for common dev terms\nconst SYNONYMS: Record<string, readonly string[]> = {\n auth: ['authentication', 'login', 'oauth', 'jwt'],\n authentication: ['auth', 'login', 'oauth'],\n login: ['auth', 'authentication', 'signin'],\n db: ['database', 'sql', 'sqlite', 'postgres'],\n database: ['db', 'sql', 'sqlite', 'postgres'],\n api: ['endpoint', 'route', 'rest', 'graphql'],\n deploy: ['deployment', 'release', 'ship', 'publish'],\n test: ['testing', 'spec', 'jest', 'vitest'],\n config: ['configuration', 'settings', 'setup'],\n err: ['error', 'exception', 'crash', 'bug'],\n error: ['err', 'exception', 'crash', 'bug'],\n};\n\n/**\n * Convert a natural language query to FTS5 query syntax.\n * Expands synonyms and wraps words in quotes for safe matching.\n */\nfunction toFtsQuery(input: string): string | null {\n const words = input\n .replace(/[^\\w\\s]/g, ' ')\n .split(/\\s+/)\n .filter(w => w.length > 0);\n\n if (words.length === 0) return null;\n\n const expanded = words.flatMap((w) => {\n const lower = w.toLowerCase();\n const syns = SYNONYMS[lower];\n return syns ? [w, ...syns] : [w];\n });\n\n return [...new Set(expanded)].map(w => `\"${w}\"`).join(' OR ');\n}\n"],"mappings":"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","names":["rows","row"]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
cwdRequire
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PGDSAUP4.js";
|
|
5
5
|
import {
|
|
6
6
|
__export
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-4JNFXVVC.js";
|
|
8
8
|
|
|
9
9
|
// src/commands/memory/config.ts
|
|
10
10
|
import { z } from "zod";
|
|
@@ -259,10 +259,37 @@ function up2(db) {
|
|
|
259
259
|
`);
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
// src/commands/memory/storage/migrations/003-add-content-hash.ts
|
|
263
|
+
var add_content_hash_exports = {};
|
|
264
|
+
__export(add_content_hash_exports, {
|
|
265
|
+
up: () => up3,
|
|
266
|
+
version: () => version3
|
|
267
|
+
});
|
|
268
|
+
import { createHash } from "crypto";
|
|
269
|
+
var version3 = 3;
|
|
270
|
+
function up3(db) {
|
|
271
|
+
db.exec("ALTER TABLE memories ADD COLUMN content_hash TEXT");
|
|
272
|
+
const rows = db.prepare("SELECT id, content FROM memories ORDER BY updated_at DESC").all();
|
|
273
|
+
const update = db.prepare("UPDATE memories SET content_hash = ? WHERE id = ?");
|
|
274
|
+
const remove = db.prepare("DELETE FROM memories WHERE id = ?");
|
|
275
|
+
const seen = /* @__PURE__ */ new Set();
|
|
276
|
+
for (const row of rows) {
|
|
277
|
+
const hash = createHash("sha256").update(row.content).digest("hex");
|
|
278
|
+
if (seen.has(hash)) {
|
|
279
|
+
remove.run(row.id);
|
|
280
|
+
} else {
|
|
281
|
+
seen.add(hash);
|
|
282
|
+
update.run(hash, row.id);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash)");
|
|
286
|
+
}
|
|
287
|
+
|
|
262
288
|
// src/commands/memory/storage/migrator.ts
|
|
263
289
|
var migrations = [
|
|
264
290
|
initial_exports,
|
|
265
|
-
add_project_exports
|
|
291
|
+
add_project_exports,
|
|
292
|
+
add_content_hash_exports
|
|
266
293
|
];
|
|
267
294
|
function getSchemaVersion(db) {
|
|
268
295
|
try {
|
|
@@ -304,4 +331,4 @@ export {
|
|
|
304
331
|
closeDatabase,
|
|
305
332
|
migrate
|
|
306
333
|
};
|
|
307
|
-
//# sourceMappingURL=chunk-
|
|
334
|
+
//# sourceMappingURL=chunk-ZXJK7CHB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/memory/config.ts","../src/commands/memory/storage/database.ts","../src/commands/memory/storage/migrations/001-initial.ts","../src/commands/memory/storage/migrations/002-add-project.ts","../src/commands/memory/storage/migrations/003-add-content-hash.ts","../src/commands/memory/storage/migrator.ts"],"sourcesContent":["import { z } from 'zod';\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { DecayParams } from './types.js';\n\n// ── Config Schema ─────────────────────────────────────────────\n\nconst ConfigSchema = z.object({\n dataDir: z.string().default('~/.agentic-memory'),\n injectionBudget: z.number().int().min(100).max(20000).default(3000),\n consolidationInterval: z.number().int().min(1).default(10),\n enableReranker: z.boolean().default(true),\n logLevel: z.enum(['debug', 'info', 'warn', 'error']).default('warn'),\n});\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\n// ── Defaults ──────────────────────────────────────────────────\n\nexport const DEFAULT_CONFIG: Config = {\n dataDir: '~/.agentic-memory',\n injectionBudget: 3000,\n consolidationInterval: 10,\n enableReranker: true,\n logLevel: 'warn',\n};\n\nexport const DEFAULT_DECAY_PARAMS: DecayParams = {\n tauByType: {\n working: 0, // cleared each session, tau irrelevant\n episodic: 30, // fast decay (was 60, cognitive science: unrehearsed episodes fade in ~30d)\n semantic: 540, // slow decay (was 365, extracted facts are stable for years)\n procedural: 730, // near-permanent\n pattern: 180, // medium decay\n },\n accessModifiers: [\n { maxCount: 3, multiplier: 1.0 },\n { maxCount: 10, multiplier: 2.0 },\n { maxCount: Infinity, multiplier: 4.0 },\n ],\n relationModifier: {\n connectedThreshold: 3,\n connectedMultiplier: 1.8, // higher tau = slower decay for connected memories\n isolatedMultiplier: 0.8, // lower tau = slightly faster decay for isolated memories\n highlyConnectedThreshold: 6,\n highlyConnectedMultiplier: 2.5, // near-immune: ~2.5x longer effective half-life\n },\n importanceFloor: 0.05,\n pruneThreshold: 0.1,\n pruneMinAgeDays: 90,\n};\n\nexport const SCORING_WEIGHTS = {\n text: 0.35,\n importance: 0.20,\n recency: 0.20,\n access: 0.10,\n context: 0.15,\n} as const;\n\n// ── Injection Algorithm Constants ──────────────────────────────\n\nexport const INJECTION_WEIGHTS = {\n context: 0.30,\n value: 0.25,\n importance: 0.20,\n recency: 0.15,\n typeBonus: 0.05,\n noise: 0.05,\n} as const;\n\nexport const TYPE_INJECTION_BONUS: Record<string, number> = {\n procedural: 1.0,\n pattern: 0.8,\n semantic: 0.6,\n episodic: 0.3,\n working: 0.0,\n};\n\nexport const RECENCY_HALF_LIFE: Record<string, number> = {\n working: 1,\n episodic: 7,\n pattern: 14,\n semantic: 30,\n procedural: 90,\n};\n\nexport const INJECTION_MIN_SCORE = 0.25;\nexport const INJECTION_COLD_START_THRESHOLD = 5;\nexport const INJECTION_COLD_START_RAMP_END = 20;\nexport const INJECTION_HEADER_TOKENS = 50;\nexport const INJECTION_MAX_SAME_TYPE_FULL = 2;\nexport const INJECTION_PINNED_BUDGET_PCT = 0.10;\n\n// ── Config Loader ─────────────────────────────────────────────\n\nexport function resolveDataDir(dataDir: string): string {\n if (dataDir.startsWith('~')) {\n return join(homedir(), dataDir.slice(1));\n }\n return dataDir;\n}\n\nexport function loadConfig(overrides?: Partial<Config>): Config {\n const envOverrides: Record<string, unknown> = {};\n\n const envBudget = process.env['AGENTIC_MEMORY_INJECTION_BUDGET'];\n if (envBudget !== undefined) {\n envOverrides['injectionBudget'] = parseInt(envBudget, 10);\n }\n\n const envLogLevel = process.env['AGENTIC_MEMORY_LOG_LEVEL'];\n if (envLogLevel !== undefined) {\n envOverrides['logLevel'] = envLogLevel;\n }\n\n const envDataDir = process.env['AGENTIC_MEMORY_DATA_DIR'];\n if (envDataDir !== undefined) {\n envOverrides['dataDir'] = envDataDir;\n }\n\n // Try loading config.json from data dir\n let fileConfig: Record<string, unknown> = {};\n const baseDir = resolveDataDir(overrides?.dataDir ?? envOverrides['dataDir'] as string ?? DEFAULT_CONFIG.dataDir);\n try {\n const raw = readFileSync(join(baseDir, 'config.json'), 'utf-8');\n fileConfig = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n const isNotFound = err instanceof Error && 'code' in err && (err as NodeJS.ErrnoException).code === 'ENOENT';\n if (!isNotFound) {\n // Malformed JSON or permissions error - warn, don't silently ignore\n console.error('[agentic-memory] Failed to load config.json:', err instanceof Error ? err.message : err);\n }\n }\n\n const merged = { ...DEFAULT_CONFIG, ...fileConfig, ...envOverrides, ...overrides };\n return ConfigSchema.parse(merged);\n}\n\n// ── Token Estimation ──────────────────────────────────────────\n\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n","import type DatabaseConstructor from 'better-sqlite3';\nimport { resolveDataDir } from '../config.js';\nimport { mkdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { cwdRequire } from '../utils/require-deps.js';\n\nexport interface DatabaseOptions {\n readonly dbPath?: string; // full path override (e.g. ':memory:' for tests)\n readonly dataDir?: string; // resolved data dir (default ~/.agentic-memory)\n}\n\nexport function createDatabase(options: DatabaseOptions = {}): DatabaseConstructor.Database {\n const dbPath = options.dbPath ?? resolveDbPath(options.dataDir);\n\n if (dbPath !== ':memory:') {\n mkdirSync(dirname(dbPath), { recursive: true });\n }\n\n const Database = cwdRequire('better-sqlite3') as typeof DatabaseConstructor;\n const sqliteVec = cwdRequire('sqlite-vec') as { load: (db: DatabaseConstructor.Database) => void };\n\n const db = new Database(dbPath);\n\n // Load sqlite-vec extension\n sqliteVec.load(db);\n\n // Configure PRAGMAs (order matters: foreign_keys before any ops, journal_mode is persistent)\n db.pragma('journal_mode = WAL');\n db.pragma('busy_timeout = 5000');\n db.pragma('foreign_keys = ON');\n db.pragma('cache_size = -64000');\n db.pragma('mmap_size = 268435456');\n db.pragma('synchronous = NORMAL');\n db.pragma('temp_store = MEMORY');\n db.pragma('journal_size_limit = 33554432');\n\n return db;\n}\n\nexport function closeDatabase(db: DatabaseConstructor.Database): void {\n try {\n db.pragma('wal_checkpoint(TRUNCATE)');\n } catch {\n // Checkpoint may fail on :memory: - that's fine\n }\n db.close();\n}\n\nfunction resolveDbPath(dataDir?: string): string {\n const dir = resolveDataDir(dataDir ?? '~/.agentic-memory');\n return join(dir, 'memory.db');\n}\n","import type Database from 'better-sqlite3';\n\nexport const version = 1;\n\nexport function up(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT\n );\n\n CREATE TABLE IF NOT EXISTS memories (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL CHECK(type IN ('episodic','semantic','procedural','working','pattern')),\n title TEXT,\n content TEXT NOT NULL,\n context TEXT,\n source TEXT CHECK(source IN ('manual','session_end','consolidation','hook','import')),\n tags TEXT NOT NULL DEFAULT '[]',\n importance REAL NOT NULL DEFAULT 0.5 CHECK(importance >= 0.0 AND importance <= 1.0),\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now')),\n access_count INTEGER NOT NULL DEFAULT 0 CHECK(access_count >= 0),\n last_accessed TEXT,\n injection_count INTEGER NOT NULL DEFAULT 0 CHECK(injection_count >= 0),\n embedding BLOB\n );\n\n CREATE TABLE IF NOT EXISTS relations (\n source_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n target_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n relation_type TEXT NOT NULL CHECK(relation_type IN (\n 'relates_to','depends_on','contradicts','extends','implements','derived_from'\n )),\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n PRIMARY KEY (source_id, target_id, relation_type)\n );\n\n -- FTS5 external content (no data duplication)\n CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(\n title, content, tags,\n content=memories,\n content_rowid=rowid,\n tokenize='porter unicode61'\n );\n\n -- FTS5 sync triggers\n CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN\n INSERT INTO memories_fts(rowid, title, content, tags)\n VALUES (new.rowid, new.title, new.content, new.tags);\n END;\n\n CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN\n INSERT INTO memories_fts(memories_fts, rowid, title, content, tags)\n VALUES ('delete', old.rowid, old.title, old.content, old.tags);\n END;\n\n CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN\n INSERT INTO memories_fts(memories_fts, rowid, title, content, tags)\n VALUES ('delete', old.rowid, old.title, old.content, old.tags);\n INSERT INTO memories_fts(rowid, title, content, tags)\n VALUES (new.rowid, new.title, new.content, new.tags);\n END;\n\n -- Vector search (synced manually in application code)\n CREATE VIRTUAL TABLE IF NOT EXISTS memories_vec USING vec0(\n memory_id TEXT PRIMARY KEY,\n embedding float[384] distance_metric=cosine\n );\n\n -- Indexes\n CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(type);\n CREATE INDEX IF NOT EXISTS idx_memories_importance ON memories(importance);\n CREATE INDEX IF NOT EXISTS idx_memories_created_at ON memories(created_at);\n CREATE INDEX IF NOT EXISTS idx_relations_target ON relations(target_id);\n `);\n}\n","import type Database from 'better-sqlite3';\n\nexport const version = 2;\n\nexport function up(db: Database.Database): void {\n db.exec(`\n ALTER TABLE memories ADD COLUMN project TEXT;\n CREATE INDEX IF NOT EXISTS idx_memories_project ON memories(project);\n `);\n}\n","import { createHash } from 'node:crypto';\nimport type Database from 'better-sqlite3';\n\nexport const version = 3;\n\nexport function up(db: Database.Database): void {\n db.exec('ALTER TABLE memories ADD COLUMN content_hash TEXT');\n\n // Backfill existing rows with SHA-256 hashes\n const rows = db.prepare('SELECT id, content FROM memories ORDER BY updated_at DESC').all() as { id: string; content: string }[];\n const update = db.prepare('UPDATE memories SET content_hash = ? WHERE id = ?');\n const remove = db.prepare('DELETE FROM memories WHERE id = ?');\n\n // Track seen hashes — keep the most recently updated, remove older duplicates\n const seen = new Set<string>();\n for (const row of rows) {\n const hash = createHash('sha256').update(row.content).digest('hex');\n if (seen.has(hash)) {\n remove.run(row.id);\n } else {\n seen.add(hash);\n update.run(hash, row.id);\n }\n }\n\n db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash)');\n}\n","import type Database from 'better-sqlite3';\nimport * as migration001 from './migrations/001-initial.js';\nimport * as migration002 from './migrations/002-add-project.js';\nimport * as migration003 from './migrations/003-add-content-hash.js';\n\ninterface Migration {\n readonly version: number;\n readonly up: (db: Database.Database) => void;\n}\n\nconst migrations: readonly Migration[] = [\n migration001,\n migration002,\n migration003,\n];\n\nexport function getSchemaVersion(db: Database.Database): number {\n try {\n const row = db.prepare(\"SELECT value FROM meta WHERE key = 'schema_version'\").get() as\n { value: string } | undefined;\n return row ? parseInt(row.value, 10) : 0;\n } catch {\n return 0;\n }\n}\n\nexport function migrate(db: Database.Database): void {\n const current = getSchemaVersion(db);\n const pending = migrations.filter(m => m.version > current);\n\n if (pending.length === 0) return;\n\n const runMigrations = db.transaction(() => {\n for (const m of pending) {\n m.up(db);\n db.prepare(\"INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)\")\n .run(String(m.version));\n }\n });\n\n runMigrations();\n}\n"],"mappings":"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","names":["join","join","up","version","up","version"]}
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
readSyncConfig
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3UJYOWGF.js";
|
|
5
5
|
import {
|
|
6
6
|
ENHANCE_SKILL_VERSION,
|
|
7
7
|
applyFixes,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
printScoreCard,
|
|
15
15
|
readFileOrNull,
|
|
16
16
|
renderDoctorReport
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-4JNFXVVC.js";
|
|
18
18
|
|
|
19
19
|
// src/cli.ts
|
|
20
20
|
import { Command as Command5 } from "commander";
|
|
@@ -239,7 +239,11 @@ function createInitCommand() {
|
|
|
239
239
|
});
|
|
240
240
|
const options = { name: name.trim(), description: description.trim() };
|
|
241
241
|
const hasClaudeMd = await fileExists(join(root, "CLAUDE.md"));
|
|
242
|
-
if (hasClaudeMd
|
|
242
|
+
if (hasClaudeMd) {
|
|
243
|
+
if (opts.yes) {
|
|
244
|
+
log.info("CLAUDE.md already exists. Use `doctor --fix` to update, or re-run without --yes to overwrite.");
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
243
247
|
const overwrite = await confirm({
|
|
244
248
|
message: "CLAUDE.md already exists. Overwrite?",
|
|
245
249
|
default: false
|
|
@@ -2031,6 +2035,13 @@ import { readFileSync } from "fs";
|
|
|
2031
2035
|
import { join as join8 } from "path";
|
|
2032
2036
|
import { Command as Command4 } from "commander";
|
|
2033
2037
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
2038
|
+
async function handleSyncErrors(fn) {
|
|
2039
|
+
try {
|
|
2040
|
+
await fn();
|
|
2041
|
+
} catch (err) {
|
|
2042
|
+
log.error(err instanceof Error ? err.message : String(err));
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2034
2045
|
function isMemoryInstalled() {
|
|
2035
2046
|
const cwd = process.cwd();
|
|
2036
2047
|
return hasMemoryHook(join8(cwd, ".claude", "settings.json")) || hasMemoryHook(join8(cwd, ".claude", "settings.local.json"));
|
|
@@ -2050,20 +2061,20 @@ function hasMemoryHook(path) {
|
|
|
2050
2061
|
}
|
|
2051
2062
|
}
|
|
2052
2063
|
function createMemoryCommand() {
|
|
2053
|
-
const memory = new Command4("memory").description("
|
|
2064
|
+
const memory = new Command4("memory").description("Project-scoped memory with decay, sync, and a TUI dashboard").option("--dashboard", "Open the memory dashboard").action(async (opts) => {
|
|
2054
2065
|
if (opts.dashboard) {
|
|
2055
2066
|
if (!isMemoryInstalled()) {
|
|
2056
2067
|
log.error("Knowledge base not set up yet. Run `claude-launchpad memory` first.");
|
|
2057
2068
|
return;
|
|
2058
2069
|
}
|
|
2059
|
-
const { requireMemoryDeps } = await import("./require-deps-
|
|
2070
|
+
const { requireMemoryDeps } = await import("./require-deps-3GIE6TAG.js");
|
|
2060
2071
|
await requireMemoryDeps();
|
|
2061
|
-
const { startTui } = await import("./tui-
|
|
2072
|
+
const { startTui } = await import("./tui-YL5NWME5.js");
|
|
2062
2073
|
await startTui();
|
|
2063
2074
|
return;
|
|
2064
2075
|
}
|
|
2065
2076
|
if (!isMemoryInstalled()) {
|
|
2066
|
-
const { detectExistingSetup } = await import("./install-
|
|
2077
|
+
const { detectExistingSetup } = await import("./install-LS7DTMIE.js");
|
|
2067
2078
|
const existing = detectExistingSetup(process.cwd());
|
|
2068
2079
|
if (existing) {
|
|
2069
2080
|
const location = existing === "local" ? ".claude/CLAUDE.md + settings.local.json" : "CLAUDE.md + settings.json";
|
|
@@ -2089,18 +2100,18 @@ function createMemoryCommand() {
|
|
|
2089
2100
|
log.info("Skipped.");
|
|
2090
2101
|
return;
|
|
2091
2102
|
}
|
|
2092
|
-
const { runInstall } = await import("./install-
|
|
2103
|
+
const { runInstall } = await import("./install-LS7DTMIE.js");
|
|
2093
2104
|
await runInstall({});
|
|
2094
2105
|
} else {
|
|
2095
|
-
const { requireMemoryDeps } = await import("./require-deps-
|
|
2106
|
+
const { requireMemoryDeps } = await import("./require-deps-3GIE6TAG.js");
|
|
2096
2107
|
await requireMemoryDeps();
|
|
2097
|
-
const { runStats } = await import("./stats-
|
|
2108
|
+
const { runStats } = await import("./stats-NQ5NRUZC.js");
|
|
2098
2109
|
await runStats({});
|
|
2099
2110
|
}
|
|
2100
2111
|
});
|
|
2101
2112
|
memory.addCommand(
|
|
2102
2113
|
new Command4("context").description("Load session context (hook handler)").option("--json", "JSON output").action(async (opts) => {
|
|
2103
|
-
const { runContext } = await import("./context-
|
|
2114
|
+
const { runContext } = await import("./context-Q5ZQBY7O.js");
|
|
2104
2115
|
await runContext(opts);
|
|
2105
2116
|
}).helpCommand(false),
|
|
2106
2117
|
{ hidden: true }
|
|
@@ -2114,21 +2125,43 @@ function createMemoryCommand() {
|
|
|
2114
2125
|
);
|
|
2115
2126
|
memory.addCommand(
|
|
2116
2127
|
new Command4("push").description("Push current project's memories to GitHub Gist").option("--all", "Push all projects").option("-y, --yes", "Skip confirmation prompt").action(async (opts) => {
|
|
2117
|
-
|
|
2118
|
-
|
|
2128
|
+
await handleSyncErrors(async () => {
|
|
2129
|
+
const { runPush } = await import("./push-BHYEETGP.js");
|
|
2130
|
+
await runPush(opts);
|
|
2131
|
+
});
|
|
2119
2132
|
})
|
|
2120
2133
|
);
|
|
2121
2134
|
memory.addCommand(
|
|
2122
2135
|
new Command4("pull").description("Pull current project's memories from GitHub Gist").option("--all", "Pull all projects").action(async (opts) => {
|
|
2123
|
-
|
|
2124
|
-
|
|
2136
|
+
await handleSyncErrors(async () => {
|
|
2137
|
+
const { runPull } = await import("./pull-4NRD4GQ4.js");
|
|
2138
|
+
await runPull(opts);
|
|
2139
|
+
});
|
|
2140
|
+
})
|
|
2141
|
+
);
|
|
2142
|
+
const sync = new Command4("sync").description("Manage memory sync");
|
|
2143
|
+
sync.addCommand(
|
|
2144
|
+
new Command4("status").description("Show local vs remote memory counts per project").action(async () => {
|
|
2145
|
+
await handleSyncErrors(async () => {
|
|
2146
|
+
const { runSyncStatus } = await import("./sync-status-ZMXMEBGC.js");
|
|
2147
|
+
await runSyncStatus();
|
|
2148
|
+
});
|
|
2149
|
+
})
|
|
2150
|
+
);
|
|
2151
|
+
sync.addCommand(
|
|
2152
|
+
new Command4("clean").description("Remove a project from the sync gist").argument("<project>", "Project slug to remove").option("-y, --yes", "Skip confirmation prompt").action(async (project, opts) => {
|
|
2153
|
+
await handleSyncErrors(async () => {
|
|
2154
|
+
const { runSyncClean } = await import("./sync-clean-QWEQVAYO.js");
|
|
2155
|
+
await runSyncClean(project, opts);
|
|
2156
|
+
});
|
|
2125
2157
|
})
|
|
2126
2158
|
);
|
|
2159
|
+
memory.addCommand(sync);
|
|
2127
2160
|
return memory;
|
|
2128
2161
|
}
|
|
2129
2162
|
|
|
2130
2163
|
// src/cli.ts
|
|
2131
|
-
var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("0.
|
|
2164
|
+
var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("1.0.1", "-v, --version").action(async () => {
|
|
2132
2165
|
const hasConfig = await fileExists(join9(process.cwd(), "CLAUDE.md")) || await fileExists(join9(process.cwd(), ".claude", "settings.json"));
|
|
2133
2166
|
if (hasConfig) {
|
|
2134
2167
|
await program.commands.find((c) => c.name() === "doctor")?.parseAsync([], { from: "user" });
|