@voidwire/lore 0.7.1 → 0.9.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/cli.ts +59 -3
- package/lib/search.ts +25 -3
- package/lib/semantic.ts +24 -4
- package/package.json +7 -7
package/cli.ts
CHANGED
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
type ObservationSubtype,
|
|
57
57
|
type ObservationConfidence,
|
|
58
58
|
} from "./index";
|
|
59
|
+
import { isValidLoreType, LORE_TYPES } from "./lib/types";
|
|
59
60
|
|
|
60
61
|
// ============================================================================
|
|
61
62
|
// Argument Parsing
|
|
@@ -204,6 +205,33 @@ async function handleSearch(args: string[]): Promise<void> {
|
|
|
204
205
|
const limit = parsed.has("limit") ? parseInt(parsed.get("limit")!, 10) : 20;
|
|
205
206
|
const since = parsed.get("since");
|
|
206
207
|
const project = parsed.get("project");
|
|
208
|
+
const type = parseList(parsed.get("type"));
|
|
209
|
+
|
|
210
|
+
// Validate type values against LoreType enum
|
|
211
|
+
if (type) {
|
|
212
|
+
const invalid = type.filter((t) => !isValidLoreType(t));
|
|
213
|
+
if (invalid.length > 0) {
|
|
214
|
+
fail(
|
|
215
|
+
`Invalid type: ${invalid.join(", ")}. Valid types: ${LORE_TYPES.join(", ")}`,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Parse --source flag (comma-separated list)
|
|
221
|
+
const sourceFilter = parseList(parsed.get("source"));
|
|
222
|
+
|
|
223
|
+
// Validate source filter values against SOURCES constant
|
|
224
|
+
if (sourceFilter) {
|
|
225
|
+
const invalid = sourceFilter.filter((s) => !SOURCES.includes(s as Source));
|
|
226
|
+
if (invalid.length > 0) {
|
|
227
|
+
fail(
|
|
228
|
+
`Invalid source: ${invalid.join(", ")}. Valid sources: ${SOURCES.join(", ")}`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Resolve effective source: --source flag takes precedence over positional arg
|
|
234
|
+
const effectiveSource: string | string[] | undefined = sourceFilter ?? source;
|
|
207
235
|
|
|
208
236
|
// Handle prismis passthrough
|
|
209
237
|
if (source === "prismis") {
|
|
@@ -250,7 +278,12 @@ async function handleSearch(args: string[]): Promise<void> {
|
|
|
250
278
|
// FTS5 path (explicit --exact only)
|
|
251
279
|
if (exact) {
|
|
252
280
|
try {
|
|
253
|
-
const results = search(query, {
|
|
281
|
+
const results = search(query, {
|
|
282
|
+
source: effectiveSource,
|
|
283
|
+
limit,
|
|
284
|
+
since,
|
|
285
|
+
type,
|
|
286
|
+
});
|
|
254
287
|
output({
|
|
255
288
|
success: true,
|
|
256
289
|
results,
|
|
@@ -278,7 +311,12 @@ async function handleSearch(args: string[]): Promise<void> {
|
|
|
278
311
|
// Semantic-only path (explicit --semantic)
|
|
279
312
|
if (semanticOnly) {
|
|
280
313
|
try {
|
|
281
|
-
const results = await semanticSearch(query, {
|
|
314
|
+
const results = await semanticSearch(query, {
|
|
315
|
+
source: effectiveSource,
|
|
316
|
+
limit,
|
|
317
|
+
project,
|
|
318
|
+
type,
|
|
319
|
+
});
|
|
282
320
|
|
|
283
321
|
if (brief) {
|
|
284
322
|
console.log(formatBriefSearch(results));
|
|
@@ -304,10 +342,11 @@ async function handleSearch(args: string[]): Promise<void> {
|
|
|
304
342
|
// Hybrid path (default) - combines vector + keyword
|
|
305
343
|
try {
|
|
306
344
|
const results = await hybridSearch(query, {
|
|
307
|
-
source,
|
|
345
|
+
source: effectiveSource,
|
|
308
346
|
limit,
|
|
309
347
|
project,
|
|
310
348
|
since,
|
|
349
|
+
type,
|
|
311
350
|
});
|
|
312
351
|
|
|
313
352
|
if (brief) {
|
|
@@ -817,6 +856,8 @@ Usage:
|
|
|
817
856
|
|
|
818
857
|
Search Options:
|
|
819
858
|
--exact Use FTS5 text search (bypasses semantic search)
|
|
859
|
+
--source <src> Filter by source (comma-separated: captures,teachings)
|
|
860
|
+
--type <types> Filter by knowledge type (gotcha, decision, learning, etc.)
|
|
820
861
|
--limit <n> Maximum results (default: 20)
|
|
821
862
|
--project <name> Filter results by project
|
|
822
863
|
--brief Compact output (titles only)
|
|
@@ -864,6 +905,10 @@ Capture Types:
|
|
|
864
905
|
|
|
865
906
|
Examples:
|
|
866
907
|
lore search "authentication"
|
|
908
|
+
lore search --source=captures "sable"
|
|
909
|
+
lore search --source=captures,teachings "schema"
|
|
910
|
+
lore search --type=gotcha "sable"
|
|
911
|
+
lore search --type=gotcha,decision "lore"
|
|
867
912
|
lore search blogs "typescript patterns"
|
|
868
913
|
lore sources
|
|
869
914
|
lore list development
|
|
@@ -888,6 +933,12 @@ Search Modes:
|
|
|
888
933
|
--semantic Vector search only
|
|
889
934
|
|
|
890
935
|
Options:
|
|
936
|
+
--source <src> Filter by source (pre-filters before search)
|
|
937
|
+
Comma-separated: --source=captures,teachings
|
|
938
|
+
--type <types> Filter by knowledge type (pre-filters before search)
|
|
939
|
+
Comma-separated: --type=gotcha,decision
|
|
940
|
+
Valid: gotcha, decision, pattern, learning, preference,
|
|
941
|
+
term, style, teaching, task, todo, idea
|
|
891
942
|
--limit <n> Maximum results (default: 20)
|
|
892
943
|
--project <name> Filter results by project/topic
|
|
893
944
|
--brief Compact output (titles only)
|
|
@@ -919,6 +970,11 @@ See also:
|
|
|
919
970
|
|
|
920
971
|
Examples:
|
|
921
972
|
lore search "authentication" # hybrid (default)
|
|
973
|
+
lore search --source=captures "sable" # filter by source
|
|
974
|
+
lore search --source=captures,teachings "schema" # multiple sources
|
|
975
|
+
lore search --source=flux --type=todo "sable" # combined filters
|
|
976
|
+
lore search --type=gotcha "sable" # filter by type
|
|
977
|
+
lore search --type=gotcha,decision "lore" # multiple types
|
|
922
978
|
lore search --exact "def process_data" # keyword only
|
|
923
979
|
lore search --semantic "login flow concepts" # vector only
|
|
924
980
|
lore search blogs "typescript patterns"
|
package/lib/search.ts
CHANGED
|
@@ -19,9 +19,10 @@ export interface SearchResult {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export interface SearchOptions {
|
|
22
|
-
source?: string;
|
|
22
|
+
source?: string | string[];
|
|
23
23
|
limit?: number;
|
|
24
24
|
since?: string;
|
|
25
|
+
type?: string | string[];
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
function getDatabasePath(): string {
|
|
@@ -69,8 +70,29 @@ export function search(
|
|
|
69
70
|
const params: (string | number)[] = [escapeFts5Query(query)];
|
|
70
71
|
|
|
71
72
|
if (options.source) {
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
const sources = Array.isArray(options.source)
|
|
74
|
+
? options.source
|
|
75
|
+
: [options.source];
|
|
76
|
+
if (sources.length === 1) {
|
|
77
|
+
conditions.push("source = ?");
|
|
78
|
+
params.push(sources[0]);
|
|
79
|
+
} else {
|
|
80
|
+
const placeholders = sources.map(() => "?").join(", ");
|
|
81
|
+
conditions.push(`source IN (${placeholders})`);
|
|
82
|
+
params.push(...sources);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (options.type) {
|
|
87
|
+
const types = Array.isArray(options.type) ? options.type : [options.type];
|
|
88
|
+
const typeClauses = types.map(
|
|
89
|
+
() =>
|
|
90
|
+
"(json_extract(metadata, '$.type') = ? OR json_extract(metadata, '$.subtype') = ?)",
|
|
91
|
+
);
|
|
92
|
+
conditions.push(`(${typeClauses.join(" OR ")})`);
|
|
93
|
+
types.forEach((t) => {
|
|
94
|
+
params.push(t, t);
|
|
95
|
+
});
|
|
74
96
|
}
|
|
75
97
|
|
|
76
98
|
if (options.since) {
|
package/lib/semantic.ts
CHANGED
|
@@ -22,9 +22,10 @@ export interface SemanticResult {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export interface SemanticSearchOptions {
|
|
25
|
-
source?: string;
|
|
25
|
+
source?: string | string[];
|
|
26
26
|
limit?: number;
|
|
27
27
|
project?: string;
|
|
28
|
+
type?: string | string[];
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -221,8 +222,17 @@ export async function semanticSearch(
|
|
|
221
222
|
params.push(limit);
|
|
222
223
|
|
|
223
224
|
if (options.source) {
|
|
224
|
-
|
|
225
|
-
|
|
225
|
+
const sources = Array.isArray(options.source)
|
|
226
|
+
? options.source
|
|
227
|
+
: [options.source];
|
|
228
|
+
if (sources.length === 1) {
|
|
229
|
+
conditions.push("e.source = ?");
|
|
230
|
+
params.push(sources[0]);
|
|
231
|
+
} else {
|
|
232
|
+
const placeholders = sources.map(() => "?").join(", ");
|
|
233
|
+
conditions.push(`e.source IN (${placeholders})`);
|
|
234
|
+
params.push(...sources);
|
|
235
|
+
}
|
|
226
236
|
}
|
|
227
237
|
|
|
228
238
|
if (options.project) {
|
|
@@ -230,6 +240,13 @@ export async function semanticSearch(
|
|
|
230
240
|
params.push(options.project);
|
|
231
241
|
}
|
|
232
242
|
|
|
243
|
+
if (options.type) {
|
|
244
|
+
const types = Array.isArray(options.type) ? options.type : [options.type];
|
|
245
|
+
const placeholders = types.map(() => "?").join(", ");
|
|
246
|
+
conditions.push(`e.type IN (${placeholders})`);
|
|
247
|
+
params.push(...types);
|
|
248
|
+
}
|
|
249
|
+
|
|
233
250
|
sql = `
|
|
234
251
|
SELECT
|
|
235
252
|
s.rowid,
|
|
@@ -270,10 +287,11 @@ export interface HybridResult {
|
|
|
270
287
|
}
|
|
271
288
|
|
|
272
289
|
export interface HybridSearchOptions {
|
|
273
|
-
source?: string;
|
|
290
|
+
source?: string | string[];
|
|
274
291
|
limit?: number;
|
|
275
292
|
project?: string;
|
|
276
293
|
since?: string;
|
|
294
|
+
type?: string | string[];
|
|
277
295
|
vectorWeight?: number;
|
|
278
296
|
textWeight?: number;
|
|
279
297
|
}
|
|
@@ -325,12 +343,14 @@ export async function hybridSearch(
|
|
|
325
343
|
source: options.source,
|
|
326
344
|
limit: fetchLimit,
|
|
327
345
|
project: options.project,
|
|
346
|
+
type: options.type,
|
|
328
347
|
}),
|
|
329
348
|
Promise.resolve(
|
|
330
349
|
keywordSearch(query, {
|
|
331
350
|
source: options.source,
|
|
332
351
|
limit: fetchLimit,
|
|
333
352
|
since: options.since,
|
|
353
|
+
type: options.type,
|
|
334
354
|
}),
|
|
335
355
|
),
|
|
336
356
|
]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voidwire/lore",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Unified knowledge CLI - Search, list, and capture your indexed knowledge",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
"README.md",
|
|
20
20
|
"LICENSE"
|
|
21
21
|
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc --noEmit false --outDir dist --declaration",
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"test": "bun test"
|
|
26
|
+
},
|
|
22
27
|
"keywords": [
|
|
23
28
|
"knowledge",
|
|
24
29
|
"search",
|
|
@@ -47,10 +52,5 @@
|
|
|
47
52
|
},
|
|
48
53
|
"devDependencies": {
|
|
49
54
|
"bun-types": "1.3.5"
|
|
50
|
-
},
|
|
51
|
-
"scripts": {
|
|
52
|
-
"build": "tsc --noEmit false --outDir dist --declaration",
|
|
53
|
-
"typecheck": "tsc --noEmit",
|
|
54
|
-
"test": "bun test"
|
|
55
55
|
}
|
|
56
|
-
}
|
|
56
|
+
}
|