@getlore/cli 0.2.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/LICENSE +13 -0
- package/README.md +80 -0
- package/dist/cli/colors.d.ts +48 -0
- package/dist/cli/colors.js +48 -0
- package/dist/cli/commands/ask.d.ts +7 -0
- package/dist/cli/commands/ask.js +97 -0
- package/dist/cli/commands/auth.d.ts +10 -0
- package/dist/cli/commands/auth.js +484 -0
- package/dist/cli/commands/daemon.d.ts +22 -0
- package/dist/cli/commands/daemon.js +244 -0
- package/dist/cli/commands/docs.d.ts +7 -0
- package/dist/cli/commands/docs.js +188 -0
- package/dist/cli/commands/extensions.d.ts +7 -0
- package/dist/cli/commands/extensions.js +204 -0
- package/dist/cli/commands/misc.d.ts +7 -0
- package/dist/cli/commands/misc.js +172 -0
- package/dist/cli/commands/pending.d.ts +7 -0
- package/dist/cli/commands/pending.js +63 -0
- package/dist/cli/commands/projects.d.ts +7 -0
- package/dist/cli/commands/projects.js +136 -0
- package/dist/cli/commands/search.d.ts +7 -0
- package/dist/cli/commands/search.js +102 -0
- package/dist/cli/commands/skills.d.ts +24 -0
- package/dist/cli/commands/skills.js +447 -0
- package/dist/cli/commands/sources.d.ts +7 -0
- package/dist/cli/commands/sources.js +121 -0
- package/dist/cli/commands/sync.d.ts +31 -0
- package/dist/cli/commands/sync.js +768 -0
- package/dist/cli/helpers.d.ts +30 -0
- package/dist/cli/helpers.js +119 -0
- package/dist/core/auth.d.ts +62 -0
- package/dist/core/auth.js +330 -0
- package/dist/core/config.d.ts +41 -0
- package/dist/core/config.js +96 -0
- package/dist/core/data-repo.d.ts +31 -0
- package/dist/core/data-repo.js +146 -0
- package/dist/core/embedder.d.ts +22 -0
- package/dist/core/embedder.js +104 -0
- package/dist/core/git.d.ts +37 -0
- package/dist/core/git.js +140 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +5 -0
- package/dist/core/insight-extractor.d.ts +26 -0
- package/dist/core/insight-extractor.js +114 -0
- package/dist/core/local-search.d.ts +43 -0
- package/dist/core/local-search.js +221 -0
- package/dist/core/themes.d.ts +15 -0
- package/dist/core/themes.js +77 -0
- package/dist/core/types.d.ts +177 -0
- package/dist/core/types.js +9 -0
- package/dist/core/user-settings.d.ts +15 -0
- package/dist/core/user-settings.js +42 -0
- package/dist/core/vector-store-lance.d.ts +98 -0
- package/dist/core/vector-store-lance.js +384 -0
- package/dist/core/vector-store-supabase.d.ts +89 -0
- package/dist/core/vector-store-supabase.js +295 -0
- package/dist/core/vector-store.d.ts +131 -0
- package/dist/core/vector-store.js +503 -0
- package/dist/daemon-runner.d.ts +8 -0
- package/dist/daemon-runner.js +246 -0
- package/dist/extensions/config.d.ts +22 -0
- package/dist/extensions/config.js +102 -0
- package/dist/extensions/proposals.d.ts +30 -0
- package/dist/extensions/proposals.js +178 -0
- package/dist/extensions/registry.d.ts +35 -0
- package/dist/extensions/registry.js +309 -0
- package/dist/extensions/sandbox.d.ts +16 -0
- package/dist/extensions/sandbox.js +17 -0
- package/dist/extensions/types.d.ts +114 -0
- package/dist/extensions/types.js +4 -0
- package/dist/extensions/worker.d.ts +1 -0
- package/dist/extensions/worker.js +49 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +105 -0
- package/dist/mcp/handlers/archive-project.d.ts +51 -0
- package/dist/mcp/handlers/archive-project.js +112 -0
- package/dist/mcp/handlers/get-quotes.d.ts +27 -0
- package/dist/mcp/handlers/get-quotes.js +61 -0
- package/dist/mcp/handlers/get-source.d.ts +9 -0
- package/dist/mcp/handlers/get-source.js +40 -0
- package/dist/mcp/handlers/ingest.d.ts +25 -0
- package/dist/mcp/handlers/ingest.js +305 -0
- package/dist/mcp/handlers/list-projects.d.ts +4 -0
- package/dist/mcp/handlers/list-projects.js +16 -0
- package/dist/mcp/handlers/list-sources.d.ts +11 -0
- package/dist/mcp/handlers/list-sources.js +20 -0
- package/dist/mcp/handlers/research-agent.d.ts +21 -0
- package/dist/mcp/handlers/research-agent.js +369 -0
- package/dist/mcp/handlers/research.d.ts +22 -0
- package/dist/mcp/handlers/research.js +225 -0
- package/dist/mcp/handlers/retain.d.ts +18 -0
- package/dist/mcp/handlers/retain.js +92 -0
- package/dist/mcp/handlers/search.d.ts +52 -0
- package/dist/mcp/handlers/search.js +145 -0
- package/dist/mcp/handlers/sync.d.ts +47 -0
- package/dist/mcp/handlers/sync.js +211 -0
- package/dist/mcp/server.d.ts +10 -0
- package/dist/mcp/server.js +268 -0
- package/dist/mcp/tools.d.ts +16 -0
- package/dist/mcp/tools.js +297 -0
- package/dist/sync/config.d.ts +26 -0
- package/dist/sync/config.js +140 -0
- package/dist/sync/discover.d.ts +51 -0
- package/dist/sync/discover.js +190 -0
- package/dist/sync/index.d.ts +11 -0
- package/dist/sync/index.js +11 -0
- package/dist/sync/process.d.ts +50 -0
- package/dist/sync/process.js +285 -0
- package/dist/sync/processors.d.ts +24 -0
- package/dist/sync/processors.js +351 -0
- package/dist/tui/browse-handlers-ask.d.ts +30 -0
- package/dist/tui/browse-handlers-ask.js +372 -0
- package/dist/tui/browse-handlers-autocomplete.d.ts +49 -0
- package/dist/tui/browse-handlers-autocomplete.js +270 -0
- package/dist/tui/browse-handlers-extensions.d.ts +18 -0
- package/dist/tui/browse-handlers-extensions.js +107 -0
- package/dist/tui/browse-handlers-pending.d.ts +22 -0
- package/dist/tui/browse-handlers-pending.js +100 -0
- package/dist/tui/browse-handlers-research.d.ts +32 -0
- package/dist/tui/browse-handlers-research.js +363 -0
- package/dist/tui/browse-handlers-tools.d.ts +42 -0
- package/dist/tui/browse-handlers-tools.js +289 -0
- package/dist/tui/browse-handlers.d.ts +239 -0
- package/dist/tui/browse-handlers.js +1944 -0
- package/dist/tui/browse-render-extensions.d.ts +14 -0
- package/dist/tui/browse-render-extensions.js +114 -0
- package/dist/tui/browse-render-tools.d.ts +18 -0
- package/dist/tui/browse-render-tools.js +259 -0
- package/dist/tui/browse-render.d.ts +51 -0
- package/dist/tui/browse-render.js +599 -0
- package/dist/tui/browse-types.d.ts +142 -0
- package/dist/tui/browse-types.js +70 -0
- package/dist/tui/browse-ui.d.ts +10 -0
- package/dist/tui/browse-ui.js +432 -0
- package/dist/tui/browse.d.ts +17 -0
- package/dist/tui/browse.js +625 -0
- package/dist/tui/markdown.d.ts +22 -0
- package/dist/tui/markdown.js +223 -0
- package/package.json +71 -0
- package/plugins/claude-code/.claude-plugin/plugin.json +10 -0
- package/plugins/claude-code/.mcp.json +6 -0
- package/plugins/claude-code/skills/lore/SKILL.md +63 -0
- package/plugins/codex/SKILL.md +36 -0
- package/plugins/codex/agents/openai.yaml +10 -0
- package/plugins/gemini/GEMINI.md +31 -0
- package/plugins/gemini/gemini-extension.json +11 -0
- package/skills/generic-agent.md +99 -0
- package/skills/openclaw.md +67 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lore - Vector Store
|
|
3
|
+
*
|
|
4
|
+
* LanceDB-based vector storage for semantic search across sources and chunks.
|
|
5
|
+
* Adapted from granola-extractor with expanded schema for projects and citations.
|
|
6
|
+
*/
|
|
7
|
+
import * as lancedb from '@lancedb/lancedb';
|
|
8
|
+
import { existsSync } from 'fs';
|
|
9
|
+
let db = null;
|
|
10
|
+
let currentDbPath = null;
|
|
11
|
+
export async function getDatabase(dbPath) {
|
|
12
|
+
// Reconnect if path changed or no connection
|
|
13
|
+
if (!db || currentDbPath !== dbPath) {
|
|
14
|
+
db = await lancedb.connect(dbPath);
|
|
15
|
+
currentDbPath = dbPath;
|
|
16
|
+
}
|
|
17
|
+
return db;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Reset the database connection to force a fresh read
|
|
21
|
+
*/
|
|
22
|
+
export function resetDatabaseConnection() {
|
|
23
|
+
db = null;
|
|
24
|
+
currentDbPath = null;
|
|
25
|
+
}
|
|
26
|
+
export async function closeDatabase() {
|
|
27
|
+
if (db) {
|
|
28
|
+
db = null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const SOURCES_TABLE = 'sources';
|
|
32
|
+
const CHUNKS_TABLE = 'chunks';
|
|
33
|
+
const PROJECTS_TABLE = 'projects';
|
|
34
|
+
const DECISIONS_TABLE = 'decisions';
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Index Management
|
|
37
|
+
// ============================================================================
|
|
38
|
+
export async function indexExists(dbPath) {
|
|
39
|
+
if (!existsSync(dbPath))
|
|
40
|
+
return false;
|
|
41
|
+
try {
|
|
42
|
+
const database = await getDatabase(dbPath);
|
|
43
|
+
const tables = await database.tableNames();
|
|
44
|
+
// Only sources table is required - chunks may be empty
|
|
45
|
+
return tables.includes(SOURCES_TABLE);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export async function initializeTables(dbPath) {
|
|
52
|
+
const database = await getDatabase(dbPath);
|
|
53
|
+
const existingTables = await database.tableNames();
|
|
54
|
+
// Drop existing tables if they exist (for reindexing)
|
|
55
|
+
for (const table of [SOURCES_TABLE, CHUNKS_TABLE]) {
|
|
56
|
+
if (existingTables.includes(table)) {
|
|
57
|
+
await database.dropTable(table);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Source Storage
|
|
63
|
+
// ============================================================================
|
|
64
|
+
/**
|
|
65
|
+
* Add a single source to the existing index (for retain operations)
|
|
66
|
+
*/
|
|
67
|
+
export async function addSource(dbPath, source, vector) {
|
|
68
|
+
const database = await getDatabase(dbPath);
|
|
69
|
+
const record = {
|
|
70
|
+
id: source.id,
|
|
71
|
+
title: source.title,
|
|
72
|
+
source_type: source.source_type,
|
|
73
|
+
content_type: source.content_type,
|
|
74
|
+
projects: source.projects,
|
|
75
|
+
tags: source.tags,
|
|
76
|
+
created_at: source.created_at,
|
|
77
|
+
summary: source.summary,
|
|
78
|
+
themes_json: source.themes_json,
|
|
79
|
+
quotes_json: source.quotes_json,
|
|
80
|
+
has_full_content: source.has_full_content,
|
|
81
|
+
vector,
|
|
82
|
+
};
|
|
83
|
+
try {
|
|
84
|
+
const table = await database.openTable(SOURCES_TABLE);
|
|
85
|
+
await table.add([record]);
|
|
86
|
+
console.error(`[addSource] Added to existing table: ${source.id}`);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
// Table doesn't exist, create it
|
|
90
|
+
console.error(`[addSource] Creating new table for: ${source.id}, error was: ${error}`);
|
|
91
|
+
await database.createTable(SOURCES_TABLE, [record]);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Add chunks to the existing index
|
|
96
|
+
*/
|
|
97
|
+
export async function addChunks(dbPath, chunks) {
|
|
98
|
+
if (chunks.length === 0)
|
|
99
|
+
return;
|
|
100
|
+
const database = await getDatabase(dbPath);
|
|
101
|
+
const records = chunks.map((chunk) => ({
|
|
102
|
+
id: chunk.id,
|
|
103
|
+
source_id: chunk.source_id,
|
|
104
|
+
content: chunk.content,
|
|
105
|
+
type: chunk.type,
|
|
106
|
+
theme_name: chunk.theme_name || '',
|
|
107
|
+
speaker: chunk.speaker || '',
|
|
108
|
+
timestamp: chunk.timestamp || '',
|
|
109
|
+
vector: chunk.vector,
|
|
110
|
+
}));
|
|
111
|
+
try {
|
|
112
|
+
const table = await database.openTable(CHUNKS_TABLE);
|
|
113
|
+
await table.add(records);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Table doesn't exist, create it
|
|
117
|
+
await database.createTable(CHUNKS_TABLE, records);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export async function storeSources(dbPath, sources) {
|
|
121
|
+
const database = await getDatabase(dbPath);
|
|
122
|
+
const records = sources.map(({ source, vector }) => ({
|
|
123
|
+
id: source.id,
|
|
124
|
+
title: source.title,
|
|
125
|
+
source_type: source.source_type,
|
|
126
|
+
content_type: source.content_type,
|
|
127
|
+
projects: source.projects,
|
|
128
|
+
tags: source.tags,
|
|
129
|
+
created_at: source.created_at,
|
|
130
|
+
summary: source.summary,
|
|
131
|
+
themes_json: source.themes_json,
|
|
132
|
+
quotes_json: source.quotes_json,
|
|
133
|
+
has_full_content: source.has_full_content,
|
|
134
|
+
vector,
|
|
135
|
+
}));
|
|
136
|
+
await database.createTable(SOURCES_TABLE, records, { mode: 'overwrite' });
|
|
137
|
+
}
|
|
138
|
+
export async function storeChunks(dbPath, chunks) {
|
|
139
|
+
if (chunks.length === 0) {
|
|
140
|
+
// Nothing to store - skip creating empty table
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const database = await getDatabase(dbPath);
|
|
144
|
+
const records = chunks.map((chunk) => ({
|
|
145
|
+
id: chunk.id,
|
|
146
|
+
source_id: chunk.source_id,
|
|
147
|
+
content: chunk.content,
|
|
148
|
+
type: chunk.type,
|
|
149
|
+
theme_name: chunk.theme_name || '',
|
|
150
|
+
speaker: chunk.speaker || '',
|
|
151
|
+
timestamp: chunk.timestamp || '',
|
|
152
|
+
vector: chunk.vector,
|
|
153
|
+
}));
|
|
154
|
+
await database.createTable(CHUNKS_TABLE, records, { mode: 'overwrite' });
|
|
155
|
+
}
|
|
156
|
+
// ============================================================================
|
|
157
|
+
// Search Operations
|
|
158
|
+
// ============================================================================
|
|
159
|
+
/**
|
|
160
|
+
* Calculate time-weighted score
|
|
161
|
+
* Recent sources get a boost, but semantic relevance is still primary
|
|
162
|
+
*/
|
|
163
|
+
function calculateTimeWeightedScore(semanticScore, createdAt, recencyBoost = 0.15) {
|
|
164
|
+
const now = Date.now();
|
|
165
|
+
const created = new Date(createdAt).getTime();
|
|
166
|
+
const ageInDays = (now - created) / (1000 * 60 * 60 * 24);
|
|
167
|
+
// Decay function: sources lose up to recencyBoost over 90 days
|
|
168
|
+
// After 90 days, no additional penalty
|
|
169
|
+
const ageFactor = Math.min(ageInDays / 90, 1);
|
|
170
|
+
const recencyScore = 1 - ageFactor * recencyBoost;
|
|
171
|
+
// Combine: semantic is primary (85%), recency is secondary (15%)
|
|
172
|
+
return semanticScore * (1 - recencyBoost) + semanticScore * recencyScore * recencyBoost;
|
|
173
|
+
}
|
|
174
|
+
export async function searchSources(dbPath, queryVector, options = {}) {
|
|
175
|
+
const { limit = 10, project, source_type, content_type, recency_boost = 0.15 } = options;
|
|
176
|
+
const database = await getDatabase(dbPath);
|
|
177
|
+
try {
|
|
178
|
+
const table = await database.openTable(SOURCES_TABLE);
|
|
179
|
+
const query = table.search(queryVector).limit(limit * 3); // Over-fetch for filtering and re-ranking
|
|
180
|
+
const results = await query.toArray();
|
|
181
|
+
const filtered = results
|
|
182
|
+
.filter((row) => {
|
|
183
|
+
if (source_type && row.source_type !== source_type)
|
|
184
|
+
return false;
|
|
185
|
+
if (content_type && row.content_type !== content_type)
|
|
186
|
+
return false;
|
|
187
|
+
if (project) {
|
|
188
|
+
const projects = JSON.parse(row.projects);
|
|
189
|
+
if (!projects.some((p) => p.toLowerCase().includes(project.toLowerCase()))) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return true;
|
|
194
|
+
})
|
|
195
|
+
.map((row) => {
|
|
196
|
+
const semanticScore = row._distance !== undefined ? 1 / (1 + row._distance) : 0;
|
|
197
|
+
const timeWeightedScore = calculateTimeWeightedScore(semanticScore, row.created_at, recency_boost);
|
|
198
|
+
return {
|
|
199
|
+
id: row.id,
|
|
200
|
+
title: row.title,
|
|
201
|
+
source_type: row.source_type,
|
|
202
|
+
content_type: row.content_type,
|
|
203
|
+
projects: JSON.parse(row.projects),
|
|
204
|
+
tags: JSON.parse(row.tags),
|
|
205
|
+
created_at: row.created_at,
|
|
206
|
+
summary: row.summary,
|
|
207
|
+
themes: JSON.parse(row.themes_json),
|
|
208
|
+
quotes: JSON.parse(row.quotes_json),
|
|
209
|
+
score: timeWeightedScore,
|
|
210
|
+
};
|
|
211
|
+
});
|
|
212
|
+
// Re-sort by time-weighted score
|
|
213
|
+
filtered.sort((a, b) => b.score - a.score);
|
|
214
|
+
return filtered.slice(0, limit);
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
console.error('Error searching sources:', error);
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
export async function searchChunks(dbPath, queryVector, options = {}) {
|
|
222
|
+
const { limit = 20, type, theme_name, source_id } = options;
|
|
223
|
+
const database = await getDatabase(dbPath);
|
|
224
|
+
try {
|
|
225
|
+
const table = await database.openTable(CHUNKS_TABLE);
|
|
226
|
+
const query = table.search(queryVector).limit(limit * 2);
|
|
227
|
+
const results = await query.toArray();
|
|
228
|
+
return results
|
|
229
|
+
.filter((row) => {
|
|
230
|
+
if (type && row.type !== type)
|
|
231
|
+
return false;
|
|
232
|
+
if (theme_name && row.theme_name !== theme_name)
|
|
233
|
+
return false;
|
|
234
|
+
if (source_id && row.source_id !== source_id)
|
|
235
|
+
return false;
|
|
236
|
+
return true;
|
|
237
|
+
})
|
|
238
|
+
.slice(0, limit)
|
|
239
|
+
.map((row) => ({
|
|
240
|
+
id: row.id,
|
|
241
|
+
source_id: row.source_id,
|
|
242
|
+
content: row.content,
|
|
243
|
+
type: row.type,
|
|
244
|
+
theme_name: row.theme_name,
|
|
245
|
+
speaker: row.speaker,
|
|
246
|
+
timestamp: row.timestamp,
|
|
247
|
+
score: row._distance !== undefined ? 1 / (1 + row._distance) : 0,
|
|
248
|
+
}));
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
// Table may not exist if no chunks have been stored
|
|
252
|
+
return [];
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// ============================================================================
|
|
256
|
+
// Retrieval Operations
|
|
257
|
+
// ============================================================================
|
|
258
|
+
export async function getAllSources(dbPath, options = {}) {
|
|
259
|
+
const { project, source_type, limit } = options;
|
|
260
|
+
const database = await getDatabase(dbPath);
|
|
261
|
+
try {
|
|
262
|
+
const table = await database.openTable(SOURCES_TABLE);
|
|
263
|
+
const results = await table.query().toArray();
|
|
264
|
+
let filtered = results.filter((row) => {
|
|
265
|
+
if (source_type && row.source_type !== source_type)
|
|
266
|
+
return false;
|
|
267
|
+
if (project) {
|
|
268
|
+
const projects = JSON.parse(row.projects);
|
|
269
|
+
if (!projects.some((p) => p.toLowerCase().includes(project.toLowerCase()))) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return true;
|
|
274
|
+
});
|
|
275
|
+
// Sort by date descending
|
|
276
|
+
filtered.sort((a, b) => new Date(b.created_at).getTime() -
|
|
277
|
+
new Date(a.created_at).getTime());
|
|
278
|
+
if (limit) {
|
|
279
|
+
filtered = filtered.slice(0, limit);
|
|
280
|
+
}
|
|
281
|
+
return filtered.map((row) => ({
|
|
282
|
+
id: row.id,
|
|
283
|
+
title: row.title,
|
|
284
|
+
source_type: row.source_type,
|
|
285
|
+
content_type: row.content_type,
|
|
286
|
+
projects: JSON.parse(row.projects),
|
|
287
|
+
created_at: row.created_at,
|
|
288
|
+
summary: row.summary,
|
|
289
|
+
}));
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
console.error('Error getting all sources:', error);
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
export async function getSourceById(dbPath, sourceId) {
|
|
297
|
+
const database = await getDatabase(dbPath);
|
|
298
|
+
try {
|
|
299
|
+
const table = await database.openTable(SOURCES_TABLE);
|
|
300
|
+
const results = await table.query().where(`id = '${sourceId}'`).toArray();
|
|
301
|
+
if (results.length === 0)
|
|
302
|
+
return null;
|
|
303
|
+
const row = results[0];
|
|
304
|
+
return {
|
|
305
|
+
id: row.id,
|
|
306
|
+
title: row.title,
|
|
307
|
+
source_type: row.source_type,
|
|
308
|
+
content_type: row.content_type,
|
|
309
|
+
projects: JSON.parse(row.projects),
|
|
310
|
+
tags: JSON.parse(row.tags),
|
|
311
|
+
created_at: row.created_at,
|
|
312
|
+
summary: row.summary,
|
|
313
|
+
themes: JSON.parse(row.themes_json),
|
|
314
|
+
quotes: JSON.parse(row.quotes_json),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
console.error('Error getting source by ID:', error);
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// ============================================================================
|
|
323
|
+
// Statistics
|
|
324
|
+
// ============================================================================
|
|
325
|
+
export async function getThemeStats(dbPath, project) {
|
|
326
|
+
const database = await getDatabase(dbPath);
|
|
327
|
+
const stats = new Map();
|
|
328
|
+
try {
|
|
329
|
+
const table = await database.openTable(SOURCES_TABLE);
|
|
330
|
+
const results = await table.query().toArray();
|
|
331
|
+
for (const row of results) {
|
|
332
|
+
// Filter by project if specified
|
|
333
|
+
if (project) {
|
|
334
|
+
const projects = JSON.parse(row.projects);
|
|
335
|
+
if (!projects.some((p) => p.toLowerCase().includes(project.toLowerCase()))) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
const themes = JSON.parse(row.themes_json);
|
|
340
|
+
for (const theme of themes) {
|
|
341
|
+
const existing = stats.get(theme.name) || { source_count: 0, quote_count: 0 };
|
|
342
|
+
existing.source_count++;
|
|
343
|
+
existing.quote_count += theme.evidence.length;
|
|
344
|
+
stats.set(theme.name, existing);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
console.error('Error getting theme stats:', error);
|
|
350
|
+
}
|
|
351
|
+
return stats;
|
|
352
|
+
}
|
|
353
|
+
export async function getProjectStats(dbPath) {
|
|
354
|
+
const database = await getDatabase(dbPath);
|
|
355
|
+
const projectMap = new Map();
|
|
356
|
+
try {
|
|
357
|
+
const table = await database.openTable(SOURCES_TABLE);
|
|
358
|
+
const results = await table.query().toArray();
|
|
359
|
+
for (const row of results) {
|
|
360
|
+
const projects = JSON.parse(row.projects);
|
|
361
|
+
const quotes = JSON.parse(row.quotes_json);
|
|
362
|
+
const created_at = row.created_at;
|
|
363
|
+
for (const project of projects) {
|
|
364
|
+
const existing = projectMap.get(project) || {
|
|
365
|
+
source_count: 0,
|
|
366
|
+
quote_count: 0,
|
|
367
|
+
latest_activity: created_at,
|
|
368
|
+
};
|
|
369
|
+
existing.source_count++;
|
|
370
|
+
existing.quote_count += quotes.length;
|
|
371
|
+
if (new Date(created_at) > new Date(existing.latest_activity)) {
|
|
372
|
+
existing.latest_activity = created_at;
|
|
373
|
+
}
|
|
374
|
+
projectMap.set(project, existing);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
379
|
+
console.error('Error getting project stats:', error);
|
|
380
|
+
}
|
|
381
|
+
return Array.from(projectMap.entries())
|
|
382
|
+
.map(([project, stats]) => ({ project, ...stats }))
|
|
383
|
+
.sort((a, b) => new Date(b.latest_activity).getTime() - new Date(a.latest_activity).getTime());
|
|
384
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lore - Vector Store (Supabase + pgvector)
|
|
3
|
+
*
|
|
4
|
+
* Cloud-hosted vector storage for semantic search across sources and chunks.
|
|
5
|
+
* Replaces LanceDB for multi-machine, multi-agent support.
|
|
6
|
+
*/
|
|
7
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
8
|
+
import type { SourceRecord, ChunkRecord, Quote, Theme, SourceType, ContentType } from './types.js';
|
|
9
|
+
export declare function indexExists(_dbPath: string): Promise<boolean>;
|
|
10
|
+
export declare function initializeTables(_dbPath: string): Promise<void>;
|
|
11
|
+
export declare function resetDatabaseConnection(): void;
|
|
12
|
+
export declare function closeDatabase(): Promise<void>;
|
|
13
|
+
export declare function getDatabase(_dbPath: string): Promise<SupabaseClient>;
|
|
14
|
+
export declare function addSource(_dbPath: string, source: SourceRecord, vector: number[]): Promise<void>;
|
|
15
|
+
export declare function addChunks(_dbPath: string, chunks: ChunkRecord[]): Promise<void>;
|
|
16
|
+
export declare function storeSources(_dbPath: string, sources: Array<{
|
|
17
|
+
source: SourceRecord;
|
|
18
|
+
vector: number[];
|
|
19
|
+
}>): Promise<void>;
|
|
20
|
+
export declare function storeChunks(_dbPath: string, chunks: ChunkRecord[]): Promise<void>;
|
|
21
|
+
export declare function searchSources(_dbPath: string, queryVector: number[], options?: {
|
|
22
|
+
limit?: number;
|
|
23
|
+
project?: string;
|
|
24
|
+
source_type?: SourceType;
|
|
25
|
+
content_type?: ContentType;
|
|
26
|
+
recency_boost?: number;
|
|
27
|
+
}): Promise<Array<{
|
|
28
|
+
id: string;
|
|
29
|
+
title: string;
|
|
30
|
+
source_type: SourceType;
|
|
31
|
+
content_type: ContentType;
|
|
32
|
+
projects: string[];
|
|
33
|
+
tags: string[];
|
|
34
|
+
created_at: string;
|
|
35
|
+
summary: string;
|
|
36
|
+
themes: Theme[];
|
|
37
|
+
quotes: Quote[];
|
|
38
|
+
score: number;
|
|
39
|
+
}>>;
|
|
40
|
+
export declare function searchChunks(_dbPath: string, queryVector: number[], options?: {
|
|
41
|
+
limit?: number;
|
|
42
|
+
type?: ChunkRecord['type'];
|
|
43
|
+
theme_name?: string;
|
|
44
|
+
source_id?: string;
|
|
45
|
+
}): Promise<Array<{
|
|
46
|
+
id: string;
|
|
47
|
+
source_id: string;
|
|
48
|
+
content: string;
|
|
49
|
+
type: string;
|
|
50
|
+
theme_name: string;
|
|
51
|
+
speaker: string;
|
|
52
|
+
timestamp: string;
|
|
53
|
+
score: number;
|
|
54
|
+
}>>;
|
|
55
|
+
export declare function getAllSources(_dbPath: string, options?: {
|
|
56
|
+
project?: string;
|
|
57
|
+
source_type?: SourceType;
|
|
58
|
+
limit?: number;
|
|
59
|
+
}): Promise<Array<{
|
|
60
|
+
id: string;
|
|
61
|
+
title: string;
|
|
62
|
+
source_type: SourceType;
|
|
63
|
+
content_type: ContentType;
|
|
64
|
+
projects: string[];
|
|
65
|
+
created_at: string;
|
|
66
|
+
summary: string;
|
|
67
|
+
}>>;
|
|
68
|
+
export declare function getSourceById(_dbPath: string, sourceId: string): Promise<{
|
|
69
|
+
id: string;
|
|
70
|
+
title: string;
|
|
71
|
+
source_type: SourceType;
|
|
72
|
+
content_type: ContentType;
|
|
73
|
+
projects: string[];
|
|
74
|
+
tags: string[];
|
|
75
|
+
created_at: string;
|
|
76
|
+
summary: string;
|
|
77
|
+
themes: Theme[];
|
|
78
|
+
quotes: Quote[];
|
|
79
|
+
} | null>;
|
|
80
|
+
export declare function getThemeStats(_dbPath: string, project?: string): Promise<Map<string, {
|
|
81
|
+
source_count: number;
|
|
82
|
+
quote_count: number;
|
|
83
|
+
}>>;
|
|
84
|
+
export declare function getProjectStats(_dbPath: string): Promise<Array<{
|
|
85
|
+
project: string;
|
|
86
|
+
source_count: number;
|
|
87
|
+
quote_count: number;
|
|
88
|
+
latest_activity: string;
|
|
89
|
+
}>>;
|