claude-launchpad 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/dist/{chunk-DTCCQWOU.js → chunk-D6RAMWH6.js} +3 -3
- package/dist/{chunk-DTCCQWOU.js.map → chunk-D6RAMWH6.js.map} +1 -1
- package/dist/chunk-HSGSTSVN.js +212 -0
- package/dist/chunk-HSGSTSVN.js.map +1 -0
- package/dist/{chunk-DXDOVWOA.js → chunk-IDH3M4EI.js} +20 -232
- package/dist/chunk-IDH3M4EI.js.map +1 -0
- package/dist/{chunk-YZ53W47Z.js → chunk-IIWOSSL4.js} +2 -1
- package/dist/chunk-IIWOSSL4.js.map +1 -0
- package/dist/{chunk-ZLI4LI33.js → chunk-M6GVFN67.js} +2 -2
- package/dist/{chunk-F6SLV2FR.js → chunk-N2SCF7DB.js} +5 -6
- package/dist/chunk-N2SCF7DB.js.map +1 -0
- package/dist/chunk-OPORVSAF.js +225 -0
- package/dist/chunk-OPORVSAF.js.map +1 -0
- package/dist/{chunk-KPO4YURF.js → chunk-QEPE3WVF.js} +457 -85
- package/dist/chunk-QEPE3WVF.js.map +1 -0
- package/dist/chunk-UN43OVRW.js +184 -0
- package/dist/chunk-UN43OVRW.js.map +1 -0
- package/dist/{chunk-PH5OWJ42.js → chunk-VF7U63AI.js} +9 -5
- package/dist/{chunk-PH5OWJ42.js.map → chunk-VF7U63AI.js.map} +1 -1
- package/dist/cli.js +307 -87
- package/dist/cli.js.map +1 -1
- package/dist/commands/memory/server.js +48 -14
- package/dist/commands/memory/server.js.map +1 -1
- package/dist/{context-JALSYDYO.js → context-YIPO4LBS.js} +58 -55
- package/dist/context-YIPO4LBS.js.map +1 -0
- package/dist/doctor-DCHFJ3XV.js +108 -0
- package/dist/doctor-DCHFJ3XV.js.map +1 -0
- package/dist/{install-Q23F37GK.js → install-D32PV7SF.js} +12 -10
- package/dist/install-D32PV7SF.js.map +1 -0
- package/dist/{pull-FYKNNLYC.js → pull-YUQTDRUP.js} +17 -19
- package/dist/pull-YUQTDRUP.js.map +1 -0
- package/dist/{push-QSBK7BQU.js → push-U4IVTFOU.js} +13 -12
- package/dist/push-U4IVTFOU.js.map +1 -0
- package/dist/{require-deps-ZISUZLYD.js → require-deps-WDMHDGBN.js} +3 -3
- package/dist/{stats-SV53G6PQ.js → stats-77BYLKYP.js} +9 -7
- package/dist/{stats-SV53G6PQ.js.map → stats-77BYLKYP.js.map} +1 -1
- package/dist/{sync-2LDM44U4.js → sync-DTCWK2CY.js} +5 -5
- package/dist/{sync-clean-HQ523N7V.js → sync-clean-ACZCB3NZ.js} +4 -5
- package/dist/sync-clean-ACZCB3NZ.js.map +1 -0
- package/dist/{sync-status-V6WTI32X.js → sync-status-2V2RCYZO.js} +13 -12
- package/dist/sync-status-2V2RCYZO.js.map +1 -0
- package/dist/{tui-2UJ2OIHB.js → tui-HVJNLRY2.js} +11 -7
- package/dist/{tui-2UJ2OIHB.js.map → tui-HVJNLRY2.js.map} +1 -1
- package/package.json +27 -7
- package/scenarios/workflow/premature-victory.yaml +44 -0
- package/dist/chunk-DXDOVWOA.js.map +0 -1
- package/dist/chunk-F6SLV2FR.js.map +0 -1
- package/dist/chunk-KPO4YURF.js.map +0 -1
- package/dist/chunk-YZ53W47Z.js.map +0 -1
- package/dist/chunk-ZIG75MOB.js +0 -366
- package/dist/chunk-ZIG75MOB.js.map +0 -1
- package/dist/context-JALSYDYO.js.map +0 -1
- package/dist/install-Q23F37GK.js.map +0 -1
- package/dist/pull-FYKNNLYC.js.map +0 -1
- package/dist/push-QSBK7BQU.js.map +0 -1
- package/dist/sync-clean-HQ523N7V.js.map +0 -1
- package/dist/sync-status-V6WTI32X.js.map +0 -1
- /package/dist/{chunk-ZLI4LI33.js.map → chunk-M6GVFN67.js.map} +0 -0
- /package/dist/{require-deps-ZISUZLYD.js.map → require-deps-WDMHDGBN.js.map} +0 -0
- /package/dist/{sync-2LDM44U4.js.map → sync-DTCWK2CY.js.map} +0 -0
package/README.md
CHANGED
|
@@ -134,7 +134,7 @@ Reads your manifest files (package.json, go.mod, pyproject.toml, etc.) and gener
|
|
|
134
134
|
|
|
135
135
|
**What init writes:**
|
|
136
136
|
- Always: `CLAUDE.md`, `TASKS.md`, `BACKLOG.md`, `.claude/settings.json`
|
|
137
|
-
- Creates when missing: `.claude/.gitignore`, `.claudeignore`, `.claude/rules/conventions.md`
|
|
137
|
+
- Creates when missing: `.claude/.gitignore`, `.claudeignore`, `.claude/rules/conventions.md`, `.claude/rules/workflow.md`, `.claude/rules/hooks.md`, `.claude/rules/verification.md`, `.claude/agents/code-reviewer.md`
|
|
138
138
|
- Offers `/lp-enhance` install (project/global/skip) if not already present
|
|
139
139
|
- CLAUDE.md includes a stop-and-swarm rule: after 3 failed attempts, Claude spins up parallel agents instead of retrying the same approach
|
|
140
140
|
|
|
@@ -216,22 +216,30 @@ CLAUDE.md rules are ~80% reliable. Hooks are 100% enforced by the harness. Init
|
|
|
216
216
|
| **workflow-check** | After BACKLOG.md/TASKS.md Edit/Write | Injects context warnings: WP entry in both files, TASKS.md > 80 lines, Current Sprint > 15 items, Session Log > 3 entries, dependency-blind pulls |
|
|
217
217
|
| **sprint-size / sprint-open** | Session start / `git commit` | Warns on microsprints (<3 WPs), oversized sprints (>7 WPs), new sprint opened without BACKLOG cleanup |
|
|
218
218
|
|
|
219
|
-
Memory projects get
|
|
219
|
+
Memory projects get three additional hooks:
|
|
220
220
|
|
|
221
221
|
| Hook | Trigger | What it does |
|
|
222
222
|
|---|---|---|
|
|
223
223
|
| **SessionStart pull** | Session opens | Auto-pulls memories from GitHub Gist |
|
|
224
|
+
| **SessionStart context** | Session opens | Injects the most relevant memories into Claude's context (6-signal scoring + diversity + token budget) |
|
|
224
225
|
| **SessionEnd push** | Session closes | Auto-pushes new memories to GitHub Gist |
|
|
225
226
|
|
|
226
227
|
## Memory (optional)
|
|
227
228
|
|
|
228
229
|
Claude's built-in memory resets per machine. Launchpad gives each project persistent, cross-device memory that syncs via a private GitHub Gist. Switch laptops and your decisions are already there.
|
|
229
230
|
|
|
231
|
+
**Why this memory and not another:**
|
|
232
|
+
|
|
233
|
+
- **Free cross-machine sync.** Native Claude Code memory is single-machine; cloud memory layers need API keys and subscriptions. Launchpad syncs through a private GitHub Gist you already have — zero cost, deletions propagate, machines converge.
|
|
234
|
+
- **Measured, not assumed.** The only Claude Code memory with a benchmark suite (59 tests) gating every release: retrieval quality, injection quality, decay accuracy, scale. `doctor` diagnoses memory health; `eval` proves behavior.
|
|
235
|
+
- **Zero infrastructure.** One native dep, stdio MCP, local SQLite. No Docker, no vector DB service, no localhost API server, no cloud account.
|
|
236
|
+
- **Engineered injection.** Session context is packed by 6-signal relevance scoring with diversity re-ranking under a token budget — not just "last N memories".
|
|
237
|
+
|
|
230
238
|
```bash
|
|
231
239
|
claude-launchpad memory
|
|
232
240
|
```
|
|
233
241
|
|
|
234
|
-
Interactive setup if not installed, stats if it is.
|
|
242
|
+
Interactive setup if not installed, stats if it is. The one native dep is installed for you (`npm install -g better-sqlite3`) or on demand. Sync requires the [GitHub CLI](https://cli.github.com/).
|
|
235
243
|
|
|
236
244
|
Once set up, `claude-launchpad memory sync` is the everyday command — pull + push in one call, so every machine has the same project brain. Deletions propagate too (tombstones): machines converge to the same memory set, never more, never less. This is the piece Claude Code's built-in memory doesn't do — see [built-in vs Launchpad memory](https://mboss37.github.io/claude-launchpad/docs/memory#built-in-memory-vs-launchpad-memory).
|
|
237
245
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
log
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-QEPE3WVF.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/memory/utils/require-deps.ts
|
|
7
7
|
import { createRequire } from "module";
|
|
@@ -31,7 +31,7 @@ async function requireMemoryDeps() {
|
|
|
31
31
|
log.blank();
|
|
32
32
|
log.info("Run this to install them:");
|
|
33
33
|
log.blank();
|
|
34
|
-
log.step(" npm install -g better-sqlite3
|
|
34
|
+
log.step(" npm install -g better-sqlite3");
|
|
35
35
|
log.blank();
|
|
36
36
|
log.info("This requires a C++ compiler (Xcode on macOS, build-essential on Linux).");
|
|
37
37
|
log.info("After installing, run `claude-launchpad memory` again.");
|
|
@@ -44,4 +44,4 @@ export {
|
|
|
44
44
|
cwdRequire,
|
|
45
45
|
requireMemoryDeps
|
|
46
46
|
};
|
|
47
|
-
//# sourceMappingURL=chunk-
|
|
47
|
+
//# sourceMappingURL=chunk-D6RAMWH6.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/memory/utils/require-deps.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { execSync } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { log } from \"../../../lib/output.js\";\n\n/**\n * Require that resolves native deps from multiple locations:\n * 1. cwd's node_modules (local project install)\n * 2. Global node_modules (npm install -g)\n * 3. CLI's own resolution chain (fallback)\n */\nexport function cwdRequire(id: string): unknown {\n // Try local project first\n try {\n const localRequire = createRequire(join(process.cwd(), \"package.json\"));\n return localRequire(id);\n } catch { /* not in local */ }\n\n // Try global node_modules\n try {\n const globalPrefix = execSync(\"npm config get prefix\", { encoding: \"utf-8\" }).trim();\n const globalRequire = createRequire(join(globalPrefix, \"lib\", \"node_modules\", \"package.json\"));\n return globalRequire(id);\n } catch { /* not in global */ }\n\n throw new Error(`Cannot find module '${id}' in local or global node_modules`);\n}\n\n/**\n * Check if memory native dependencies are available.\n * Called at the start of any subcommand that needs SQLite.\n * Returns true if deps are available, exits with helpful message if not.\n */\nexport async function requireMemoryDeps(): Promise<boolean> {\n try {\n cwdRequire(\"better-sqlite3\");\n return true;\n } catch {\n log.blank();\n log.error(\"Memory system requires native dependencies that are not installed.\");\n log.blank();\n log.info(\"Run this to install them:\");\n log.blank();\n log.step(\" npm install -g better-sqlite3
|
|
1
|
+
{"version":3,"sources":["../src/commands/memory/utils/require-deps.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { execSync } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { log } from \"../../../lib/output.js\";\n\n/**\n * Require that resolves native deps from multiple locations:\n * 1. cwd's node_modules (local project install)\n * 2. Global node_modules (npm install -g)\n * 3. CLI's own resolution chain (fallback)\n */\nexport function cwdRequire(id: string): unknown {\n // Try local project first\n try {\n const localRequire = createRequire(join(process.cwd(), \"package.json\"));\n return localRequire(id);\n } catch { /* not in local */ }\n\n // Try global node_modules\n try {\n const globalPrefix = execSync(\"npm config get prefix\", { encoding: \"utf-8\" }).trim();\n const globalRequire = createRequire(join(globalPrefix, \"lib\", \"node_modules\", \"package.json\"));\n return globalRequire(id);\n } catch { /* not in global */ }\n\n throw new Error(`Cannot find module '${id}' in local or global node_modules`);\n}\n\n/**\n * Check if memory native dependencies are available.\n * Called at the start of any subcommand that needs SQLite.\n * Returns true if deps are available, exits with helpful message if not.\n */\nexport async function requireMemoryDeps(): Promise<boolean> {\n try {\n cwdRequire(\"better-sqlite3\");\n return true;\n } catch {\n log.blank();\n log.error(\"Memory system requires native dependencies that are not installed.\");\n log.blank();\n log.info(\"Run this to install them:\");\n log.blank();\n log.step(\" npm install -g better-sqlite3\");\n log.blank();\n log.info(\"This requires a C++ compiler (Xcode on macOS, build-essential on Linux).\");\n log.info(\"After installing, run `claude-launchpad memory` again.\");\n log.blank();\n process.exit(1);\n }\n}\n\n"],"mappings":";;;;;;AAAA,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB;AACzB,SAAS,YAAY;AASd,SAAS,WAAW,IAAqB;AAE9C,MAAI;AACF,UAAM,eAAe,cAAc,KAAK,QAAQ,IAAI,GAAG,cAAc,CAAC;AACtE,WAAO,aAAa,EAAE;AAAA,EACxB,QAAQ;AAAA,EAAqB;AAG7B,MAAI;AACF,UAAM,eAAe,SAAS,yBAAyB,EAAE,UAAU,QAAQ,CAAC,EAAE,KAAK;AACnF,UAAM,gBAAgB,cAAc,KAAK,cAAc,OAAO,gBAAgB,cAAc,CAAC;AAC7F,WAAO,cAAc,EAAE;AAAA,EACzB,QAAQ;AAAA,EAAsB;AAE9B,QAAM,IAAI,MAAM,uBAAuB,EAAE,mCAAmC;AAC9E;AAOA,eAAsB,oBAAsC;AAC1D,MAAI;AACF,eAAW,gBAAgB;AAC3B,WAAO;AAAA,EACT,QAAQ;AACN,QAAI,MAAM;AACV,QAAI,MAAM,oEAAoE;AAC9E,QAAI,MAAM;AACV,QAAI,KAAK,2BAA2B;AACpC,QAAI,MAAM;AACV,QAAI,KAAK,iCAAiC;AAC1C,QAAI,MAAM;AACV,QAAI,KAAK,0EAA0E;AACnF,QAAI,KAAK,wDAAwD;AACjE,QAAI,MAAM;AACV,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/commands/memory/storage/relation-repo.ts
|
|
4
|
+
function rowToRelation(row) {
|
|
5
|
+
return {
|
|
6
|
+
sourceId: row.source_id,
|
|
7
|
+
targetId: row.target_id,
|
|
8
|
+
relationType: row.relation_type,
|
|
9
|
+
createdAt: row.created_at
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
var RelationRepo = class {
|
|
13
|
+
#stmts;
|
|
14
|
+
constructor(db) {
|
|
15
|
+
this.#stmts = {
|
|
16
|
+
insert: db.prepare(`
|
|
17
|
+
INSERT OR IGNORE INTO relations (source_id, target_id, relation_type)
|
|
18
|
+
VALUES (?, ?, ?)
|
|
19
|
+
`),
|
|
20
|
+
getBySource: db.prepare("SELECT * FROM relations WHERE source_id = ?"),
|
|
21
|
+
getByTarget: db.prepare("SELECT * FROM relations WHERE target_id = ?"),
|
|
22
|
+
getByMemory: db.prepare(`
|
|
23
|
+
SELECT * FROM relations WHERE source_id = ? OR target_id = ?
|
|
24
|
+
`),
|
|
25
|
+
delete: db.prepare(`
|
|
26
|
+
DELETE FROM relations WHERE source_id = ? AND target_id = ? AND relation_type = ?
|
|
27
|
+
`),
|
|
28
|
+
countByMemory: db.prepare(`
|
|
29
|
+
SELECT COUNT(*) as count FROM relations WHERE source_id = ? OR target_id = ?
|
|
30
|
+
`),
|
|
31
|
+
count: db.prepare("SELECT COUNT(*) as count FROM relations"),
|
|
32
|
+
getAll: db.prepare("SELECT * FROM relations"),
|
|
33
|
+
deleteOrphaned: db.prepare(`
|
|
34
|
+
DELETE FROM relations
|
|
35
|
+
WHERE source_id NOT IN (SELECT id FROM memories)
|
|
36
|
+
OR target_id NOT IN (SELECT id FROM memories)
|
|
37
|
+
`)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
create(sourceId, targetId, relationType) {
|
|
41
|
+
const result = this.#stmts.insert.run(sourceId, targetId, relationType);
|
|
42
|
+
return result.changes > 0;
|
|
43
|
+
}
|
|
44
|
+
getBySource(sourceId) {
|
|
45
|
+
const rows = this.#stmts.getBySource.all(sourceId);
|
|
46
|
+
return rows.map(rowToRelation);
|
|
47
|
+
}
|
|
48
|
+
getByTarget(targetId) {
|
|
49
|
+
const rows = this.#stmts.getByTarget.all(targetId);
|
|
50
|
+
return rows.map(rowToRelation);
|
|
51
|
+
}
|
|
52
|
+
getByMemory(memoryId) {
|
|
53
|
+
const rows = this.#stmts.getByMemory.all(memoryId, memoryId);
|
|
54
|
+
return rows.map(rowToRelation);
|
|
55
|
+
}
|
|
56
|
+
delete(sourceId, targetId, relationType) {
|
|
57
|
+
const result = this.#stmts.delete.run(sourceId, targetId, relationType);
|
|
58
|
+
return result.changes > 0;
|
|
59
|
+
}
|
|
60
|
+
countByMemory(memoryId) {
|
|
61
|
+
const row = this.#stmts.countByMemory.get(memoryId, memoryId);
|
|
62
|
+
return row.count;
|
|
63
|
+
}
|
|
64
|
+
count() {
|
|
65
|
+
const row = this.#stmts.count.get();
|
|
66
|
+
return row.count;
|
|
67
|
+
}
|
|
68
|
+
getAll() {
|
|
69
|
+
const rows = this.#stmts.getAll.all();
|
|
70
|
+
return rows.map(rowToRelation);
|
|
71
|
+
}
|
|
72
|
+
deleteOrphaned() {
|
|
73
|
+
const result = this.#stmts.deleteOrphaned.run();
|
|
74
|
+
return result.changes;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/commands/memory/storage/search-repo.ts
|
|
79
|
+
var SearchRepo = class {
|
|
80
|
+
#stmts;
|
|
81
|
+
constructor(db) {
|
|
82
|
+
this.#stmts = {
|
|
83
|
+
ftsSearch: db.prepare(`
|
|
84
|
+
SELECT
|
|
85
|
+
m.rowid,
|
|
86
|
+
m.id as memory_id,
|
|
87
|
+
bm25(memories_fts, 5.0, 1.0, 2.0) as rank
|
|
88
|
+
FROM memories_fts f
|
|
89
|
+
JOIN memories m ON m.rowid = f.rowid
|
|
90
|
+
WHERE memories_fts MATCH @query
|
|
91
|
+
ORDER BY rank
|
|
92
|
+
LIMIT @limit
|
|
93
|
+
`),
|
|
94
|
+
ftsSearchByProject: db.prepare(`
|
|
95
|
+
SELECT
|
|
96
|
+
m.rowid,
|
|
97
|
+
m.id as memory_id,
|
|
98
|
+
bm25(memories_fts, 5.0, 1.0, 2.0) as rank
|
|
99
|
+
FROM memories_fts f
|
|
100
|
+
JOIN memories m ON m.rowid = f.rowid
|
|
101
|
+
WHERE memories_fts MATCH @query
|
|
102
|
+
AND (m.project = @project OR m.project IS NULL)
|
|
103
|
+
ORDER BY rank
|
|
104
|
+
LIMIT @limit
|
|
105
|
+
`),
|
|
106
|
+
ftsSearchFiltered: db.prepare(`
|
|
107
|
+
SELECT
|
|
108
|
+
m.rowid,
|
|
109
|
+
m.id as memory_id,
|
|
110
|
+
bm25(memories_fts, 5.0, 1.0, 2.0) as rank
|
|
111
|
+
FROM memories_fts f
|
|
112
|
+
JOIN memories m ON m.rowid = f.rowid
|
|
113
|
+
WHERE memories_fts MATCH @query
|
|
114
|
+
AND m.type = @type
|
|
115
|
+
AND m.importance >= @minImportance
|
|
116
|
+
ORDER BY rank
|
|
117
|
+
LIMIT @limit
|
|
118
|
+
`),
|
|
119
|
+
ftsSearchFilteredByProject: db.prepare(`
|
|
120
|
+
SELECT
|
|
121
|
+
m.rowid,
|
|
122
|
+
m.id as memory_id,
|
|
123
|
+
bm25(memories_fts, 5.0, 1.0, 2.0) as rank
|
|
124
|
+
FROM memories_fts f
|
|
125
|
+
JOIN memories m ON m.rowid = f.rowid
|
|
126
|
+
WHERE memories_fts MATCH @query
|
|
127
|
+
AND m.type = @type
|
|
128
|
+
AND m.importance >= @minImportance
|
|
129
|
+
AND (m.project = @project OR m.project IS NULL)
|
|
130
|
+
ORDER BY rank
|
|
131
|
+
LIMIT @limit
|
|
132
|
+
`)
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Full-text search using BM25 ranking.
|
|
137
|
+
* Returns matches sorted by relevance (most relevant first).
|
|
138
|
+
*/
|
|
139
|
+
searchFts(options) {
|
|
140
|
+
const ftsQuery = toFtsQuery(options.query);
|
|
141
|
+
if (!ftsQuery) return [];
|
|
142
|
+
try {
|
|
143
|
+
const hasType = !!options.type;
|
|
144
|
+
const hasProject = !!options.project;
|
|
145
|
+
let rows;
|
|
146
|
+
if (hasType && hasProject) {
|
|
147
|
+
rows = this.#stmts.ftsSearchFilteredByProject.all({
|
|
148
|
+
query: ftsQuery,
|
|
149
|
+
limit: options.limit,
|
|
150
|
+
type: options.type,
|
|
151
|
+
minImportance: options.minImportance ?? 0,
|
|
152
|
+
project: options.project
|
|
153
|
+
});
|
|
154
|
+
} else if (hasType) {
|
|
155
|
+
rows = this.#stmts.ftsSearchFiltered.all({
|
|
156
|
+
query: ftsQuery,
|
|
157
|
+
limit: options.limit,
|
|
158
|
+
type: options.type,
|
|
159
|
+
minImportance: options.minImportance ?? 0
|
|
160
|
+
});
|
|
161
|
+
} else if (hasProject) {
|
|
162
|
+
rows = this.#stmts.ftsSearchByProject.all({
|
|
163
|
+
query: ftsQuery,
|
|
164
|
+
limit: options.limit,
|
|
165
|
+
project: options.project
|
|
166
|
+
});
|
|
167
|
+
} else {
|
|
168
|
+
rows = this.#stmts.ftsSearch.all({
|
|
169
|
+
query: ftsQuery,
|
|
170
|
+
limit: options.limit
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
return rows.map((r) => ({
|
|
174
|
+
rowid: r.rowid,
|
|
175
|
+
memoryId: r.memory_id,
|
|
176
|
+
rank: r.rank
|
|
177
|
+
}));
|
|
178
|
+
} catch (err) {
|
|
179
|
+
console.error("[agentic-memory] FTS5 search error:", err instanceof Error ? err.message : err);
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
var SYNONYMS = {
|
|
185
|
+
auth: ["authentication", "login", "oauth", "jwt"],
|
|
186
|
+
authentication: ["auth", "login", "oauth"],
|
|
187
|
+
login: ["auth", "authentication", "signin"],
|
|
188
|
+
db: ["database", "sql", "sqlite", "postgres"],
|
|
189
|
+
database: ["db", "sql", "sqlite", "postgres"],
|
|
190
|
+
api: ["endpoint", "route", "rest", "graphql"],
|
|
191
|
+
deploy: ["deployment", "release", "ship", "publish"],
|
|
192
|
+
test: ["testing", "spec", "jest", "vitest"],
|
|
193
|
+
config: ["configuration", "settings", "setup"],
|
|
194
|
+
err: ["error", "exception", "crash", "bug"],
|
|
195
|
+
error: ["err", "exception", "crash", "bug"]
|
|
196
|
+
};
|
|
197
|
+
function toFtsQuery(input) {
|
|
198
|
+
const words = input.replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 0);
|
|
199
|
+
if (words.length === 0) return null;
|
|
200
|
+
const expanded = words.flatMap((w) => {
|
|
201
|
+
const lower = w.toLowerCase();
|
|
202
|
+
const syns = SYNONYMS[lower];
|
|
203
|
+
return syns ? [w, ...syns] : [w];
|
|
204
|
+
});
|
|
205
|
+
return [...new Set(expanded)].map((w) => `"${w}"`).join(" OR ");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export {
|
|
209
|
+
RelationRepo,
|
|
210
|
+
SearchRepo
|
|
211
|
+
};
|
|
212
|
+
//# sourceMappingURL=chunk-HSGSTSVN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/memory/storage/relation-repo.ts","../src/commands/memory/storage/search-repo.ts"],"sourcesContent":["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":[]}
|
|
@@ -24,6 +24,7 @@ function rowToMemory(row) {
|
|
|
24
24
|
project: row.project,
|
|
25
25
|
tags: safeParseTags(row.tags),
|
|
26
26
|
importance: row.importance,
|
|
27
|
+
baseImportance: row.base_importance ?? row.importance,
|
|
27
28
|
createdAt: row.created_at,
|
|
28
29
|
updatedAt: row.updated_at,
|
|
29
30
|
accessCount: row.access_count,
|
|
@@ -38,8 +39,8 @@ var MemoryRepo = class {
|
|
|
38
39
|
this.db = db;
|
|
39
40
|
this.#stmts = {
|
|
40
41
|
insert: db.prepare(`
|
|
41
|
-
INSERT OR IGNORE INTO memories (id, type, title, content, context, source, project, tags, importance, created_at, updated_at,
|
|
42
|
-
VALUES (@id, @type, @title, @content, @context, @source, @project, @tags, @importance, @
|
|
42
|
+
INSERT OR IGNORE INTO memories (id, type, title, content, context, source, project, tags, importance, base_importance, created_at, updated_at, content_hash)
|
|
43
|
+
VALUES (@id, @type, @title, @content, @context, @source, @project, @tags, @importance, @importance, @createdAt, @updatedAt, @contentHash)
|
|
43
44
|
`),
|
|
44
45
|
getById: db.prepare("SELECT * FROM memories WHERE id = ?"),
|
|
45
46
|
getAll: db.prepare("SELECT * FROM memories ORDER BY created_at DESC"),
|
|
@@ -52,11 +53,10 @@ var MemoryRepo = class {
|
|
|
52
53
|
update: db.prepare(`
|
|
53
54
|
UPDATE memories
|
|
54
55
|
SET title = @title, content = @content, context = @context, tags = @tags,
|
|
55
|
-
importance = @importance,
|
|
56
|
+
importance = @importance, base_importance = @baseImportance, updated_at = @updatedAt,
|
|
56
57
|
content_hash = @contentHash
|
|
57
58
|
WHERE id = @id
|
|
58
59
|
`),
|
|
59
|
-
updateImportance: db.prepare("UPDATE memories SET importance = ?, updated_at = ? WHERE id = ?"),
|
|
60
60
|
updateImportanceOnly: db.prepare("UPDATE memories SET importance = ? WHERE id = ?"),
|
|
61
61
|
incrementAccess: db.prepare(`
|
|
62
62
|
UPDATE memories SET access_count = access_count + 1, last_accessed = ? WHERE id = ?
|
|
@@ -64,7 +64,7 @@ var MemoryRepo = class {
|
|
|
64
64
|
incrementInjection: db.prepare(`
|
|
65
65
|
UPDATE memories SET injection_count = injection_count + 1 WHERE id = ?
|
|
66
66
|
`),
|
|
67
|
-
softDelete: db.prepare("UPDATE memories SET importance = 0, updated_at = ? WHERE id = ?"),
|
|
67
|
+
softDelete: db.prepare("UPDATE memories SET importance = 0, base_importance = 0, updated_at = ? WHERE id = ?"),
|
|
68
68
|
hardDelete: db.prepare("DELETE FROM memories WHERE id = ?"),
|
|
69
69
|
deleteByType: db.prepare("DELETE FROM memories WHERE type = ?"),
|
|
70
70
|
deleteByProject: db.prepare("DELETE FROM memories WHERE project = ?"),
|
|
@@ -79,16 +79,16 @@ var MemoryRepo = class {
|
|
|
79
79
|
`),
|
|
80
80
|
insertSync: db.prepare(`
|
|
81
81
|
INSERT OR IGNORE INTO memories
|
|
82
|
-
(id, type, title, content, context, source, project, tags, importance,
|
|
83
|
-
access_count, injection_count, created_at, updated_at, last_accessed,
|
|
84
|
-
VALUES (@id, @type, @title, @content, @context, @source, @project, @tags, @importance,
|
|
85
|
-
@accessCount, @injectionCount, @createdAt, @updatedAt, @lastAccessed,
|
|
82
|
+
(id, type, title, content, context, source, project, tags, importance, base_importance,
|
|
83
|
+
access_count, injection_count, created_at, updated_at, last_accessed, content_hash)
|
|
84
|
+
VALUES (@id, @type, @title, @content, @context, @source, @project, @tags, @importance, @baseImportance,
|
|
85
|
+
@accessCount, @injectionCount, @createdAt, @updatedAt, @lastAccessed, @contentHash)
|
|
86
86
|
`),
|
|
87
87
|
updateSync: db.prepare(`
|
|
88
88
|
UPDATE memories SET
|
|
89
89
|
title = @title, content = @content, context = @context,
|
|
90
90
|
source = @source, project = @project, tags = @tags,
|
|
91
|
-
importance = @importance, access_count = @accessCount,
|
|
91
|
+
importance = @importance, base_importance = @baseImportance, access_count = @accessCount,
|
|
92
92
|
injection_count = @injectionCount, updated_at = @updatedAt,
|
|
93
93
|
last_accessed = @lastAccessed, content_hash = @contentHash
|
|
94
94
|
WHERE id = @id
|
|
@@ -114,7 +114,7 @@ var MemoryRepo = class {
|
|
|
114
114
|
tombstoneDelete: db.prepare("DELETE FROM memory_tombstones WHERE id = ?")
|
|
115
115
|
};
|
|
116
116
|
}
|
|
117
|
-
create(input
|
|
117
|
+
create(input) {
|
|
118
118
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
119
119
|
const id = randomUUID();
|
|
120
120
|
const contentHash = createHash("sha256").update(input.content).digest("hex");
|
|
@@ -130,7 +130,6 @@ var MemoryRepo = class {
|
|
|
130
130
|
importance: input.importance,
|
|
131
131
|
createdAt: now,
|
|
132
132
|
updatedAt: now,
|
|
133
|
-
embedding: null,
|
|
134
133
|
contentHash
|
|
135
134
|
};
|
|
136
135
|
const result = this.#stmts.insert.run(params);
|
|
@@ -145,12 +144,12 @@ var MemoryRepo = class {
|
|
|
145
144
|
project: input.project ?? null,
|
|
146
145
|
tags: JSON.stringify(input.tags),
|
|
147
146
|
importance: input.importance,
|
|
147
|
+
base_importance: input.importance,
|
|
148
148
|
created_at: now,
|
|
149
149
|
updated_at: now,
|
|
150
150
|
access_count: 0,
|
|
151
151
|
last_accessed: null,
|
|
152
|
-
injection_count: 0
|
|
153
|
-
embedding: null
|
|
152
|
+
injection_count: 0
|
|
154
153
|
};
|
|
155
154
|
return rowToMemory(row);
|
|
156
155
|
}
|
|
@@ -198,18 +197,14 @@ var MemoryRepo = class {
|
|
|
198
197
|
context: updates.context !== void 0 ? updates.context : existing.context,
|
|
199
198
|
tags: JSON.stringify(updates.tags ?? existing.tags),
|
|
200
199
|
importance: updates.importance ?? existing.importance,
|
|
200
|
+
// Content edits must not erode the anchor; only explicit re-rating moves it.
|
|
201
|
+
baseImportance: updates.importance ?? existing.baseImportance,
|
|
201
202
|
updatedAt: now,
|
|
202
|
-
embedding: null,
|
|
203
203
|
contentHash: createHash("sha256").update(finalContent).digest("hex")
|
|
204
204
|
};
|
|
205
205
|
this.#stmts.update.run(params);
|
|
206
206
|
return true;
|
|
207
207
|
}
|
|
208
|
-
updateImportance(id, importance) {
|
|
209
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
210
|
-
const result = this.#stmts.updateImportance.run(importance, now, id);
|
|
211
|
-
return result.changes > 0;
|
|
212
|
-
}
|
|
213
208
|
/** Update importance without touching updated_at - used by decay to avoid resetting the clock. */
|
|
214
209
|
updateImportanceOnly(id, importance) {
|
|
215
210
|
const result = this.#stmts.updateImportanceOnly.run(importance, id);
|
|
@@ -309,6 +304,7 @@ var MemoryRepo = class {
|
|
|
309
304
|
project: row.project,
|
|
310
305
|
tags: JSON.stringify(row.tags),
|
|
311
306
|
importance: row.importance,
|
|
307
|
+
baseImportance: row.base_importance ?? row.importance,
|
|
312
308
|
accessCount: row.access_count,
|
|
313
309
|
injectionCount: row.injection_count,
|
|
314
310
|
createdAt: row.created_at,
|
|
@@ -318,10 +314,9 @@ var MemoryRepo = class {
|
|
|
318
314
|
};
|
|
319
315
|
const existing = this.getById(row.id);
|
|
320
316
|
if (existing) {
|
|
321
|
-
this.#stmts.updateSync.run(params);
|
|
322
|
-
} else {
|
|
323
|
-
this.#stmts.insertSync.run(params);
|
|
317
|
+
return this.#stmts.updateSync.run(params).changes > 0;
|
|
324
318
|
}
|
|
319
|
+
return this.#stmts.insertSync.run(params).changes > 0;
|
|
325
320
|
}
|
|
326
321
|
getAllForSync(project) {
|
|
327
322
|
if (project) {
|
|
@@ -332,214 +327,7 @@ var MemoryRepo = class {
|
|
|
332
327
|
}
|
|
333
328
|
};
|
|
334
329
|
|
|
335
|
-
// src/commands/memory/storage/relation-repo.ts
|
|
336
|
-
function rowToRelation(row) {
|
|
337
|
-
return {
|
|
338
|
-
sourceId: row.source_id,
|
|
339
|
-
targetId: row.target_id,
|
|
340
|
-
relationType: row.relation_type,
|
|
341
|
-
createdAt: row.created_at
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
var RelationRepo = class {
|
|
345
|
-
#stmts;
|
|
346
|
-
constructor(db) {
|
|
347
|
-
this.#stmts = {
|
|
348
|
-
insert: db.prepare(`
|
|
349
|
-
INSERT OR IGNORE INTO relations (source_id, target_id, relation_type)
|
|
350
|
-
VALUES (?, ?, ?)
|
|
351
|
-
`),
|
|
352
|
-
getBySource: db.prepare("SELECT * FROM relations WHERE source_id = ?"),
|
|
353
|
-
getByTarget: db.prepare("SELECT * FROM relations WHERE target_id = ?"),
|
|
354
|
-
getByMemory: db.prepare(`
|
|
355
|
-
SELECT * FROM relations WHERE source_id = ? OR target_id = ?
|
|
356
|
-
`),
|
|
357
|
-
delete: db.prepare(`
|
|
358
|
-
DELETE FROM relations WHERE source_id = ? AND target_id = ? AND relation_type = ?
|
|
359
|
-
`),
|
|
360
|
-
countByMemory: db.prepare(`
|
|
361
|
-
SELECT COUNT(*) as count FROM relations WHERE source_id = ? OR target_id = ?
|
|
362
|
-
`),
|
|
363
|
-
count: db.prepare("SELECT COUNT(*) as count FROM relations"),
|
|
364
|
-
getAll: db.prepare("SELECT * FROM relations"),
|
|
365
|
-
deleteOrphaned: db.prepare(`
|
|
366
|
-
DELETE FROM relations
|
|
367
|
-
WHERE source_id NOT IN (SELECT id FROM memories)
|
|
368
|
-
OR target_id NOT IN (SELECT id FROM memories)
|
|
369
|
-
`)
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
create(sourceId, targetId, relationType) {
|
|
373
|
-
const result = this.#stmts.insert.run(sourceId, targetId, relationType);
|
|
374
|
-
return result.changes > 0;
|
|
375
|
-
}
|
|
376
|
-
getBySource(sourceId) {
|
|
377
|
-
const rows = this.#stmts.getBySource.all(sourceId);
|
|
378
|
-
return rows.map(rowToRelation);
|
|
379
|
-
}
|
|
380
|
-
getByTarget(targetId) {
|
|
381
|
-
const rows = this.#stmts.getByTarget.all(targetId);
|
|
382
|
-
return rows.map(rowToRelation);
|
|
383
|
-
}
|
|
384
|
-
getByMemory(memoryId) {
|
|
385
|
-
const rows = this.#stmts.getByMemory.all(memoryId, memoryId);
|
|
386
|
-
return rows.map(rowToRelation);
|
|
387
|
-
}
|
|
388
|
-
delete(sourceId, targetId, relationType) {
|
|
389
|
-
const result = this.#stmts.delete.run(sourceId, targetId, relationType);
|
|
390
|
-
return result.changes > 0;
|
|
391
|
-
}
|
|
392
|
-
countByMemory(memoryId) {
|
|
393
|
-
const row = this.#stmts.countByMemory.get(memoryId, memoryId);
|
|
394
|
-
return row.count;
|
|
395
|
-
}
|
|
396
|
-
count() {
|
|
397
|
-
const row = this.#stmts.count.get();
|
|
398
|
-
return row.count;
|
|
399
|
-
}
|
|
400
|
-
getAll() {
|
|
401
|
-
const rows = this.#stmts.getAll.all();
|
|
402
|
-
return rows.map(rowToRelation);
|
|
403
|
-
}
|
|
404
|
-
deleteOrphaned() {
|
|
405
|
-
const result = this.#stmts.deleteOrphaned.run();
|
|
406
|
-
return result.changes;
|
|
407
|
-
}
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
// src/commands/memory/storage/search-repo.ts
|
|
411
|
-
var SearchRepo = class {
|
|
412
|
-
#stmts;
|
|
413
|
-
constructor(db) {
|
|
414
|
-
this.#stmts = {
|
|
415
|
-
ftsSearch: db.prepare(`
|
|
416
|
-
SELECT
|
|
417
|
-
m.rowid,
|
|
418
|
-
m.id as memory_id,
|
|
419
|
-
bm25(memories_fts, 5.0, 1.0, 2.0) as rank
|
|
420
|
-
FROM memories_fts f
|
|
421
|
-
JOIN memories m ON m.rowid = f.rowid
|
|
422
|
-
WHERE memories_fts MATCH @query
|
|
423
|
-
ORDER BY rank
|
|
424
|
-
LIMIT @limit
|
|
425
|
-
`),
|
|
426
|
-
ftsSearchByProject: db.prepare(`
|
|
427
|
-
SELECT
|
|
428
|
-
m.rowid,
|
|
429
|
-
m.id as memory_id,
|
|
430
|
-
bm25(memories_fts, 5.0, 1.0, 2.0) as rank
|
|
431
|
-
FROM memories_fts f
|
|
432
|
-
JOIN memories m ON m.rowid = f.rowid
|
|
433
|
-
WHERE memories_fts MATCH @query
|
|
434
|
-
AND (m.project = @project OR m.project IS NULL)
|
|
435
|
-
ORDER BY rank
|
|
436
|
-
LIMIT @limit
|
|
437
|
-
`),
|
|
438
|
-
ftsSearchFiltered: db.prepare(`
|
|
439
|
-
SELECT
|
|
440
|
-
m.rowid,
|
|
441
|
-
m.id as memory_id,
|
|
442
|
-
bm25(memories_fts, 5.0, 1.0, 2.0) as rank
|
|
443
|
-
FROM memories_fts f
|
|
444
|
-
JOIN memories m ON m.rowid = f.rowid
|
|
445
|
-
WHERE memories_fts MATCH @query
|
|
446
|
-
AND m.type = @type
|
|
447
|
-
AND m.importance >= @minImportance
|
|
448
|
-
ORDER BY rank
|
|
449
|
-
LIMIT @limit
|
|
450
|
-
`),
|
|
451
|
-
ftsSearchFilteredByProject: db.prepare(`
|
|
452
|
-
SELECT
|
|
453
|
-
m.rowid,
|
|
454
|
-
m.id as memory_id,
|
|
455
|
-
bm25(memories_fts, 5.0, 1.0, 2.0) as rank
|
|
456
|
-
FROM memories_fts f
|
|
457
|
-
JOIN memories m ON m.rowid = f.rowid
|
|
458
|
-
WHERE memories_fts MATCH @query
|
|
459
|
-
AND m.type = @type
|
|
460
|
-
AND m.importance >= @minImportance
|
|
461
|
-
AND (m.project = @project OR m.project IS NULL)
|
|
462
|
-
ORDER BY rank
|
|
463
|
-
LIMIT @limit
|
|
464
|
-
`)
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
|
-
/**
|
|
468
|
-
* Full-text search using BM25 ranking.
|
|
469
|
-
* Returns matches sorted by relevance (most relevant first).
|
|
470
|
-
*/
|
|
471
|
-
searchFts(options) {
|
|
472
|
-
const ftsQuery = toFtsQuery(options.query);
|
|
473
|
-
if (!ftsQuery) return [];
|
|
474
|
-
try {
|
|
475
|
-
const hasType = !!options.type;
|
|
476
|
-
const hasProject = !!options.project;
|
|
477
|
-
let rows;
|
|
478
|
-
if (hasType && hasProject) {
|
|
479
|
-
rows = this.#stmts.ftsSearchFilteredByProject.all({
|
|
480
|
-
query: ftsQuery,
|
|
481
|
-
limit: options.limit,
|
|
482
|
-
type: options.type,
|
|
483
|
-
minImportance: options.minImportance ?? 0,
|
|
484
|
-
project: options.project
|
|
485
|
-
});
|
|
486
|
-
} else if (hasType) {
|
|
487
|
-
rows = this.#stmts.ftsSearchFiltered.all({
|
|
488
|
-
query: ftsQuery,
|
|
489
|
-
limit: options.limit,
|
|
490
|
-
type: options.type,
|
|
491
|
-
minImportance: options.minImportance ?? 0
|
|
492
|
-
});
|
|
493
|
-
} else if (hasProject) {
|
|
494
|
-
rows = this.#stmts.ftsSearchByProject.all({
|
|
495
|
-
query: ftsQuery,
|
|
496
|
-
limit: options.limit,
|
|
497
|
-
project: options.project
|
|
498
|
-
});
|
|
499
|
-
} else {
|
|
500
|
-
rows = this.#stmts.ftsSearch.all({
|
|
501
|
-
query: ftsQuery,
|
|
502
|
-
limit: options.limit
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
return rows.map((r) => ({
|
|
506
|
-
rowid: r.rowid,
|
|
507
|
-
memoryId: r.memory_id,
|
|
508
|
-
rank: r.rank
|
|
509
|
-
}));
|
|
510
|
-
} catch (err) {
|
|
511
|
-
console.error("[agentic-memory] FTS5 search error:", err instanceof Error ? err.message : err);
|
|
512
|
-
return [];
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
};
|
|
516
|
-
var SYNONYMS = {
|
|
517
|
-
auth: ["authentication", "login", "oauth", "jwt"],
|
|
518
|
-
authentication: ["auth", "login", "oauth"],
|
|
519
|
-
login: ["auth", "authentication", "signin"],
|
|
520
|
-
db: ["database", "sql", "sqlite", "postgres"],
|
|
521
|
-
database: ["db", "sql", "sqlite", "postgres"],
|
|
522
|
-
api: ["endpoint", "route", "rest", "graphql"],
|
|
523
|
-
deploy: ["deployment", "release", "ship", "publish"],
|
|
524
|
-
test: ["testing", "spec", "jest", "vitest"],
|
|
525
|
-
config: ["configuration", "settings", "setup"],
|
|
526
|
-
err: ["error", "exception", "crash", "bug"],
|
|
527
|
-
error: ["err", "exception", "crash", "bug"]
|
|
528
|
-
};
|
|
529
|
-
function toFtsQuery(input) {
|
|
530
|
-
const words = input.replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 0);
|
|
531
|
-
if (words.length === 0) return null;
|
|
532
|
-
const expanded = words.flatMap((w) => {
|
|
533
|
-
const lower = w.toLowerCase();
|
|
534
|
-
const syns = SYNONYMS[lower];
|
|
535
|
-
return syns ? [w, ...syns] : [w];
|
|
536
|
-
});
|
|
537
|
-
return [...new Set(expanded)].map((w) => `"${w}"`).join(" OR ");
|
|
538
|
-
}
|
|
539
|
-
|
|
540
330
|
export {
|
|
541
|
-
MemoryRepo
|
|
542
|
-
RelationRepo,
|
|
543
|
-
SearchRepo
|
|
331
|
+
MemoryRepo
|
|
544
332
|
};
|
|
545
|
-
//# sourceMappingURL=chunk-
|
|
333
|
+
//# sourceMappingURL=chunk-IDH3M4EI.js.map
|