@voidwire/lore 0.6.0 → 0.6.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/lib/list.ts CHANGED
@@ -71,7 +71,7 @@ const PROJECT_FIELD: Record<string, string> = {
71
71
  commits: "project",
72
72
  sessions: "project",
73
73
  tasks: "project",
74
- insights: "project",
74
+ insights: "topic",
75
75
  captures: "topic",
76
76
  teachings: "topic",
77
77
  learnings: "topic",
@@ -161,23 +161,28 @@ function queryPersonalType(
161
161
  type: string,
162
162
  limit?: number,
163
163
  ): ListEntry[] {
164
- // Query personal source, then filter by type in metadata
165
- const sql = limit
166
- ? `SELECT title, content, metadata FROM search WHERE source = 'personal' LIMIT ?`
167
- : `SELECT title, content, metadata FROM search WHERE source = 'personal'`;
164
+ // Filter by type in SQL, not JS - avoids LIMIT truncation bug
165
+ let sql = `
166
+ SELECT title, content, metadata FROM search
167
+ WHERE source = 'personal'
168
+ AND json_extract(metadata, '$.type') = ?
169
+ ORDER BY json_extract(metadata, '$.timestamp') DESC
170
+ `;
171
+ const params: (string | number)[] = [type];
168
172
 
169
- const stmt = db.prepare(sql);
170
- const rows = (limit ? stmt.all(limit * 10) : stmt.all()) as RawRow[]; // Over-fetch for filtering
173
+ if (limit) {
174
+ sql += " LIMIT ?";
175
+ params.push(limit);
176
+ }
171
177
 
172
- const filtered = rows
173
- .map((row) => ({
174
- title: row.title,
175
- content: row.content,
176
- metadata: JSON.parse(row.metadata || "{}"),
177
- }))
178
- .filter((entry) => entry.metadata.type === type);
178
+ const stmt = db.prepare(sql);
179
+ const rows = stmt.all(...params) as RawRow[];
179
180
 
180
- return limit ? filtered.slice(0, limit) : filtered;
181
+ return rows.map((row) => ({
182
+ title: row.title,
183
+ content: row.content,
184
+ metadata: JSON.parse(row.metadata || "{}"),
185
+ }));
181
186
  }
182
187
 
183
188
  /**
package/lib/projects.ts CHANGED
@@ -14,7 +14,7 @@ const PROJECT_FIELD: Record<string, string> = {
14
14
  commits: "project",
15
15
  sessions: "project",
16
16
  tasks: "project",
17
- insights: "project",
17
+ insights: "topic",
18
18
  captures: "topic",
19
19
  teachings: "topic",
20
20
  learnings: "topic",
package/lib/semantic.ts CHANGED
@@ -43,7 +43,7 @@ const PROJECT_FIELD: Record<string, string> = {
43
43
  commits: "project",
44
44
  sessions: "project",
45
45
  tasks: "project",
46
- insights: "project",
46
+ insights: "topic",
47
47
  captures: "topic",
48
48
  teachings: "topic",
49
49
  learnings: "topic",
@@ -193,70 +193,42 @@ export async function semanticSearch(
193
193
 
194
194
  // KNN query - 1:1 mapping between search rows and embeddings
195
195
  // Content is pre-chunked at ingest time
196
+ // source/topic partition columns enable filtered KNN (filter BEFORE search)
196
197
  let sql: string;
197
198
  const params: (Uint8Array | string | number)[] = [queryBlob];
198
199
 
200
+ // Build KNN query with optional partition filters
201
+ const conditions = ["e.embedding MATCH ?", "k = ?"];
202
+ params.push(limit);
203
+
199
204
  if (options.source) {
200
- // Filter by e.source (partition column) for KNN pre-filtering
201
- // This filters BEFORE KNN, not after — critical for domain-specific search
202
- sql = `
203
- SELECT
204
- s.source,
205
- s.title,
206
- s.content,
207
- s.metadata,
208
- e.distance
209
- FROM embeddings e
210
- JOIN search s ON e.doc_id = s.rowid
211
- WHERE e.embedding MATCH ?
212
- AND k = ?
213
- AND e.source = ?
214
- ORDER BY e.distance
215
- LIMIT ?
216
- `;
217
- params.push(limit);
205
+ conditions.push("e.source = ?");
218
206
  params.push(options.source);
219
- params.push(limit);
220
- } else {
221
- sql = `
222
- SELECT
223
- s.source,
224
- s.title,
225
- s.content,
226
- s.metadata,
227
- e.distance
228
- FROM embeddings e
229
- JOIN search s ON e.doc_id = s.rowid
230
- WHERE e.embedding MATCH ?
231
- AND k = ?
232
- ORDER BY e.distance
233
- LIMIT ?
234
- `;
235
- params.push(limit);
236
- params.push(limit);
237
207
  }
238
208
 
239
- const stmt = db.prepare(sql);
240
- const results = stmt.all(...params) as SemanticResult[];
241
-
242
- // Post-filter by project if specified
243
- // KNN WHERE clause doesn't support json_extract on joined metadata,
244
- // so we filter after the query returns
245
209
  if (options.project) {
246
- return results.filter((result) => {
247
- const field = PROJECT_FIELD[result.source];
248
- if (!field) return false;
249
-
250
- try {
251
- const metadata = JSON.parse(result.metadata);
252
- return metadata[field] === options.project;
253
- } catch {
254
- // Skip results with malformed metadata
255
- return false;
256
- }
257
- });
210
+ conditions.push("e.topic = ?");
211
+ params.push(options.project);
258
212
  }
259
213
 
214
+ sql = `
215
+ SELECT
216
+ s.source,
217
+ s.title,
218
+ s.content,
219
+ s.metadata,
220
+ e.distance
221
+ FROM embeddings e
222
+ JOIN search s ON e.doc_id = s.rowid
223
+ WHERE ${conditions.join("\n AND ")}
224
+ ORDER BY e.distance
225
+ LIMIT ?
226
+ `;
227
+ params.push(limit);
228
+
229
+ const stmt = db.prepare(sql);
230
+ const results = stmt.all(...params) as SemanticResult[];
231
+
260
232
  return results;
261
233
  } finally {
262
234
  db.close();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidwire/lore",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Unified knowledge CLI - Search, list, and capture your indexed knowledge",
5
5
  "type": "module",
6
6
  "main": "./index.ts",