@voidwire/lore 0.1.13 → 0.1.15
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/cli.ts +9 -2
- package/lib/list.ts +30 -6
- package/lib/semantic.ts +31 -0
- package/package.json +1 -1
package/cli.ts
CHANGED
|
@@ -185,6 +185,7 @@ async function handleSearch(args: string[]): Promise<void> {
|
|
|
185
185
|
|
|
186
186
|
const limit = parsed.has("limit") ? parseInt(parsed.get("limit")!, 10) : 20;
|
|
187
187
|
const since = parsed.get("since");
|
|
188
|
+
const project = parsed.get("project");
|
|
188
189
|
|
|
189
190
|
// Handle prismis passthrough
|
|
190
191
|
if (source === "prismis") {
|
|
@@ -255,7 +256,7 @@ async function handleSearch(args: string[]): Promise<void> {
|
|
|
255
256
|
}
|
|
256
257
|
|
|
257
258
|
try {
|
|
258
|
-
const results = await semanticSearch(query, { source, limit });
|
|
259
|
+
const results = await semanticSearch(query, { source, limit, project });
|
|
259
260
|
output({
|
|
260
261
|
success: true,
|
|
261
262
|
results,
|
|
@@ -318,9 +319,10 @@ function handleList(args: string[]): void {
|
|
|
318
319
|
? parseInt(parsed.get("limit")!, 10)
|
|
319
320
|
: undefined;
|
|
320
321
|
const format = parsed.get("format") || "json";
|
|
322
|
+
const project = parsed.get("project");
|
|
321
323
|
|
|
322
324
|
try {
|
|
323
|
-
const result = list(domain, { limit });
|
|
325
|
+
const result = list(domain, { limit, project });
|
|
324
326
|
|
|
325
327
|
if (format === "human") {
|
|
326
328
|
console.log(formatHumanOutput(result));
|
|
@@ -580,6 +582,7 @@ Usage:
|
|
|
580
582
|
Search Options:
|
|
581
583
|
--exact Use FTS5 text search (bypasses semantic search)
|
|
582
584
|
--limit <n> Maximum results (default: 20)
|
|
585
|
+
--project <name> Filter results by project
|
|
583
586
|
--since <date> Filter by date (today, yesterday, this-week, YYYY-MM-DD)
|
|
584
587
|
--sources List indexed sources with counts
|
|
585
588
|
|
|
@@ -637,6 +640,7 @@ Usage:
|
|
|
637
640
|
Options:
|
|
638
641
|
--exact Use FTS5 text search (bypasses semantic search)
|
|
639
642
|
--limit <n> Maximum results (default: 20)
|
|
643
|
+
--project <name> Filter results by project (post-filters KNN results)
|
|
640
644
|
--since <date> Filter by date (today, yesterday, this-week, YYYY-MM-DD)
|
|
641
645
|
--sources List indexed sources with counts
|
|
642
646
|
--help Show this help
|
|
@@ -664,6 +668,7 @@ Examples:
|
|
|
664
668
|
lore search "authentication"
|
|
665
669
|
lore search blogs "typescript patterns"
|
|
666
670
|
lore search commits --since this-week "refactor"
|
|
671
|
+
lore search "authentication" --project=momentum --limit 5
|
|
667
672
|
lore search --exact "def process_data"
|
|
668
673
|
lore search prismis "kubernetes security"
|
|
669
674
|
lore search atuin "docker build"
|
|
@@ -682,6 +687,7 @@ Usage:
|
|
|
682
687
|
Options:
|
|
683
688
|
--limit <n> Maximum entries (default: all)
|
|
684
689
|
--format <fmt> Output format: json (default), jsonl, human
|
|
690
|
+
--project <name> Filter by project name
|
|
685
691
|
--domains List available domains
|
|
686
692
|
--help Show this help
|
|
687
693
|
|
|
@@ -707,6 +713,7 @@ Available Domains:
|
|
|
707
713
|
Examples:
|
|
708
714
|
lore list development
|
|
709
715
|
lore list commits --limit 10 --format human
|
|
716
|
+
lore list commits --project=momentum --limit 5
|
|
710
717
|
lore list books --format jsonl
|
|
711
718
|
`);
|
|
712
719
|
process.exit(0);
|
package/lib/list.ts
CHANGED
|
@@ -59,8 +59,18 @@ const PERSONAL_SUBTYPES: Partial<Record<Domain, string>> = {
|
|
|
59
59
|
habits: "habit",
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
// Maps source to metadata field containing project name
|
|
63
|
+
const PROJECT_FIELD: Record<string, string> = {
|
|
64
|
+
commits: "project",
|
|
65
|
+
sessions: "project",
|
|
66
|
+
tasks: "project",
|
|
67
|
+
captures: "context",
|
|
68
|
+
teachings: "source",
|
|
69
|
+
};
|
|
70
|
+
|
|
62
71
|
export interface ListOptions {
|
|
63
72
|
limit?: number;
|
|
73
|
+
project?: string;
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
export interface ListEntry {
|
|
@@ -93,13 +103,27 @@ function queryBySource(
|
|
|
93
103
|
db: Database,
|
|
94
104
|
source: string,
|
|
95
105
|
limit?: number,
|
|
106
|
+
project?: string,
|
|
96
107
|
): ListEntry[] {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
108
|
+
let sql = "SELECT title, content, metadata FROM search WHERE source = ?";
|
|
109
|
+
const params: (string | number)[] = [source];
|
|
110
|
+
|
|
111
|
+
// Add project filter if provided and source has a project field
|
|
112
|
+
if (project) {
|
|
113
|
+
const field = PROJECT_FIELD[source];
|
|
114
|
+
if (field) {
|
|
115
|
+
sql += ` AND json_extract(metadata, '$.${field}') = ?`;
|
|
116
|
+
params.push(project);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (limit) {
|
|
121
|
+
sql += " LIMIT ?";
|
|
122
|
+
params.push(limit);
|
|
123
|
+
}
|
|
100
124
|
|
|
101
|
-
const stmt =
|
|
102
|
-
const rows =
|
|
125
|
+
const stmt = db.prepare(sql);
|
|
126
|
+
const rows = stmt.all(...params) as RawRow[];
|
|
103
127
|
|
|
104
128
|
return rows.map((row) => ({
|
|
105
129
|
title: row.title,
|
|
@@ -166,7 +190,7 @@ export function list(domain: Domain, options: ListOptions = {}): ListResult {
|
|
|
166
190
|
if (personalType) {
|
|
167
191
|
entries = queryPersonalType(db, personalType, options.limit);
|
|
168
192
|
} else {
|
|
169
|
-
entries = queryBySource(db, domain, options.limit);
|
|
193
|
+
entries = queryBySource(db, domain, options.limit, options.project);
|
|
170
194
|
}
|
|
171
195
|
|
|
172
196
|
return {
|
package/lib/semantic.ts
CHANGED
|
@@ -32,8 +32,21 @@ export interface SemanticResult {
|
|
|
32
32
|
export interface SemanticSearchOptions {
|
|
33
33
|
source?: string;
|
|
34
34
|
limit?: number;
|
|
35
|
+
project?: string;
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Maps source types to their project field name in metadata JSON.
|
|
40
|
+
* Different sources store project names in different fields.
|
|
41
|
+
*/
|
|
42
|
+
const PROJECT_FIELD: Record<string, string> = {
|
|
43
|
+
commits: "project",
|
|
44
|
+
sessions: "project",
|
|
45
|
+
tasks: "project",
|
|
46
|
+
captures: "context",
|
|
47
|
+
teachings: "source",
|
|
48
|
+
};
|
|
49
|
+
|
|
37
50
|
const MODEL_NAME = "nomic-ai/nomic-embed-text-v1.5";
|
|
38
51
|
|
|
39
52
|
interface EmbeddingPipeline {
|
|
@@ -223,6 +236,24 @@ export async function semanticSearch(
|
|
|
223
236
|
const stmt = db.prepare(sql);
|
|
224
237
|
const results = stmt.all(...params) as SemanticResult[];
|
|
225
238
|
|
|
239
|
+
// Post-filter by project if specified
|
|
240
|
+
// KNN WHERE clause doesn't support json_extract on joined metadata,
|
|
241
|
+
// so we filter after the query returns
|
|
242
|
+
if (options.project) {
|
|
243
|
+
return results.filter((result) => {
|
|
244
|
+
const field = PROJECT_FIELD[result.source];
|
|
245
|
+
if (!field) return false;
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
const metadata = JSON.parse(result.metadata);
|
|
249
|
+
return metadata[field] === options.project;
|
|
250
|
+
} catch {
|
|
251
|
+
// Skip results with malformed metadata
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
226
257
|
return results;
|
|
227
258
|
} finally {
|
|
228
259
|
db.close();
|