@voidwire/lore 0.1.4 → 0.1.6
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 +80 -28
- package/index.ts +2 -0
- package/lib/capture.ts +39 -1
- package/lib/semantic.ts +8 -10
- package/package.json +1 -1
package/cli.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* lore search <query> Search all sources
|
|
12
12
|
* lore search <source> <query> Search specific source
|
|
13
13
|
* lore list <domain> List domain entries
|
|
14
|
-
* lore capture task|knowledge|note
|
|
14
|
+
* lore capture task|knowledge|note|teaching Capture knowledge
|
|
15
15
|
*
|
|
16
16
|
* Exit codes:
|
|
17
17
|
* 0 - Success
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
captureTask,
|
|
30
30
|
captureKnowledge,
|
|
31
31
|
captureNote,
|
|
32
|
+
captureTeaching,
|
|
32
33
|
semanticSearch,
|
|
33
34
|
isOllamaAvailable,
|
|
34
35
|
hasEmbeddings,
|
|
@@ -40,6 +41,7 @@ import {
|
|
|
40
41
|
type TaskInput,
|
|
41
42
|
type KnowledgeInput,
|
|
42
43
|
type NoteInput,
|
|
44
|
+
type TeachingInput,
|
|
43
45
|
type KnowledgeCaptureType,
|
|
44
46
|
} from "./index";
|
|
45
47
|
|
|
@@ -224,48 +226,51 @@ async function handleSearch(args: string[]): Promise<void> {
|
|
|
224
226
|
return;
|
|
225
227
|
}
|
|
226
228
|
|
|
227
|
-
//
|
|
228
|
-
if (
|
|
229
|
+
// FTS5 path (explicit --exact only)
|
|
230
|
+
if (exact) {
|
|
229
231
|
try {
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
mode: "semantic",
|
|
238
|
-
});
|
|
239
|
-
console.error(
|
|
240
|
-
`✅ ${results.length} result${results.length !== 1 ? "s" : ""} found (semantic)`,
|
|
241
|
-
);
|
|
242
|
-
process.exit(0);
|
|
243
|
-
}
|
|
244
|
-
// Fall through to FTS5 if semantic not available
|
|
245
|
-
} catch (error) {
|
|
246
|
-
// Semantic search failed, fall back to FTS5
|
|
232
|
+
const results = search(query, { source, limit, since });
|
|
233
|
+
output({
|
|
234
|
+
success: true,
|
|
235
|
+
results,
|
|
236
|
+
count: results.length,
|
|
237
|
+
mode: "exact",
|
|
238
|
+
});
|
|
247
239
|
console.error(
|
|
248
|
-
|
|
240
|
+
`✅ ${results.length} result${results.length !== 1 ? "s" : ""} found (exact)`,
|
|
249
241
|
);
|
|
242
|
+
process.exit(0);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
245
|
+
fail(message, 2);
|
|
250
246
|
}
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Semantic path (default) - fail if unavailable
|
|
251
|
+
if (!hasEmbeddings()) {
|
|
252
|
+
fail("No embeddings found. Run lore-embed-all first.", 2);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (!(await isOllamaAvailable())) {
|
|
256
|
+
fail("Ollama not available. Start Ollama or check SQLITE_VEC_PATH.", 2);
|
|
251
257
|
}
|
|
252
258
|
|
|
253
|
-
// FTS5 path (default fallback or explicit --exact)
|
|
254
259
|
try {
|
|
255
|
-
const results =
|
|
260
|
+
const results = await semanticSearch(query, { source, limit });
|
|
256
261
|
output({
|
|
257
262
|
success: true,
|
|
258
263
|
results,
|
|
259
264
|
count: results.length,
|
|
260
|
-
mode:
|
|
265
|
+
mode: "semantic",
|
|
261
266
|
});
|
|
262
267
|
console.error(
|
|
263
|
-
`✅ ${results.length} result${results.length !== 1 ? "s" : ""} found (
|
|
268
|
+
`✅ ${results.length} result${results.length !== 1 ? "s" : ""} found (semantic)`,
|
|
264
269
|
);
|
|
265
270
|
process.exit(0);
|
|
266
271
|
} catch (error) {
|
|
267
272
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
268
|
-
fail(message
|
|
273
|
+
fail(`Semantic search failed: ${message}`, 2);
|
|
269
274
|
}
|
|
270
275
|
}
|
|
271
276
|
|
|
@@ -433,6 +438,34 @@ function handleCaptureNote(args: string[]): void {
|
|
|
433
438
|
}
|
|
434
439
|
}
|
|
435
440
|
|
|
441
|
+
function handleCaptureTeaching(args: string[]): void {
|
|
442
|
+
const parsed = parseArgs(args);
|
|
443
|
+
|
|
444
|
+
const required = ["domain", "confidence", "text"];
|
|
445
|
+
const missing = required.filter((f) => !parsed.has(f));
|
|
446
|
+
if (missing.length > 0) {
|
|
447
|
+
fail(`Missing required fields: ${missing.join(", ")}`);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const input: TeachingInput = {
|
|
451
|
+
domain: parsed.get("domain")!,
|
|
452
|
+
confidence: parsed.get("confidence")!,
|
|
453
|
+
text: parsed.get("text")!,
|
|
454
|
+
source: parsed.get("source"),
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
const result = captureTeaching(input);
|
|
458
|
+
output(result);
|
|
459
|
+
|
|
460
|
+
if (result.success) {
|
|
461
|
+
console.error("✅ Teaching logged");
|
|
462
|
+
process.exit(0);
|
|
463
|
+
} else {
|
|
464
|
+
console.error(`❌ ${result.error}`);
|
|
465
|
+
process.exit(2);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
436
469
|
function handleCapture(args: string[]): void {
|
|
437
470
|
if (hasFlag(args, "help")) {
|
|
438
471
|
showCaptureHelp();
|
|
@@ -455,9 +488,12 @@ function handleCapture(args: string[]): void {
|
|
|
455
488
|
case "note":
|
|
456
489
|
handleCaptureNote(captureArgs);
|
|
457
490
|
break;
|
|
491
|
+
case "teaching":
|
|
492
|
+
handleCaptureTeaching(captureArgs);
|
|
493
|
+
break;
|
|
458
494
|
default:
|
|
459
495
|
fail(
|
|
460
|
-
`Unknown capture type: ${captureType}. Use: task, knowledge, or
|
|
496
|
+
`Unknown capture type: ${captureType}. Use: task, knowledge, note, or teaching`,
|
|
461
497
|
);
|
|
462
498
|
}
|
|
463
499
|
}
|
|
@@ -481,7 +517,7 @@ Usage:
|
|
|
481
517
|
lore search --sources List indexed sources
|
|
482
518
|
lore list <domain> List domain entries
|
|
483
519
|
lore list --domains List available domains
|
|
484
|
-
lore capture task|knowledge|note
|
|
520
|
+
lore capture task|knowledge|note|teaching Capture knowledge
|
|
485
521
|
|
|
486
522
|
Search Options:
|
|
487
523
|
--exact Use FTS5 text search (bypasses semantic search)
|
|
@@ -515,6 +551,12 @@ Capture Types:
|
|
|
515
551
|
--tags Comma-separated tags
|
|
516
552
|
--context Optional context
|
|
517
553
|
|
|
554
|
+
teaching Log teaching/learning
|
|
555
|
+
--domain Subject area (required)
|
|
556
|
+
--confidence Certainty level (required)
|
|
557
|
+
--text Teaching content (required)
|
|
558
|
+
--source Optional source identifier
|
|
559
|
+
|
|
518
560
|
Examples:
|
|
519
561
|
lore search "authentication"
|
|
520
562
|
lore search blogs "typescript patterns"
|
|
@@ -620,6 +662,7 @@ Usage:
|
|
|
620
662
|
lore capture task Log task completion
|
|
621
663
|
lore capture knowledge Log insight/learning
|
|
622
664
|
lore capture note Quick note
|
|
665
|
+
lore capture teaching Log teaching moment
|
|
623
666
|
|
|
624
667
|
Capture Types:
|
|
625
668
|
|
|
@@ -651,10 +694,19 @@ Capture Types:
|
|
|
651
694
|
--tags Comma-separated tags
|
|
652
695
|
--context Optional context
|
|
653
696
|
|
|
697
|
+
teaching - Log teaching or learning moment
|
|
698
|
+
Required:
|
|
699
|
+
--domain Subject area (e.g., typescript, architecture)
|
|
700
|
+
--confidence Certainty level (e.g., high, medium, low)
|
|
701
|
+
--text Teaching content
|
|
702
|
+
Optional:
|
|
703
|
+
--source Source identifier (defaults to "manual")
|
|
704
|
+
|
|
654
705
|
Examples:
|
|
655
706
|
lore capture task --project=lore --name="Add help" --problem="No subcommand help" --solution="Added per-command help functions"
|
|
656
707
|
lore capture knowledge --context=lore --text="Unified CLI works" --type=learning
|
|
657
708
|
lore capture note --text="Remember to update docs" --tags=docs,todo
|
|
709
|
+
lore capture teaching --domain=patterns --confidence=high --text="Prefer composition over inheritance"
|
|
658
710
|
`);
|
|
659
711
|
process.exit(0);
|
|
660
712
|
}
|
package/index.ts
CHANGED
|
@@ -46,11 +46,13 @@ export {
|
|
|
46
46
|
captureKnowledge,
|
|
47
47
|
captureTask,
|
|
48
48
|
captureNote,
|
|
49
|
+
captureTeaching,
|
|
49
50
|
type CaptureResult,
|
|
50
51
|
type KnowledgeInput,
|
|
51
52
|
type KnowledgeCaptureType,
|
|
52
53
|
type TaskInput,
|
|
53
54
|
type NoteInput,
|
|
55
|
+
type TeachingInput,
|
|
54
56
|
type CaptureEvent,
|
|
55
57
|
} from "./lib/capture";
|
|
56
58
|
|
package/lib/capture.ts
CHANGED
|
@@ -50,6 +50,13 @@ export interface NoteInput {
|
|
|
50
50
|
context?: string;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
export interface TeachingInput {
|
|
54
|
+
domain: string;
|
|
55
|
+
confidence: string;
|
|
56
|
+
text: string;
|
|
57
|
+
source?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
53
60
|
interface TaskEvent {
|
|
54
61
|
event: "captured";
|
|
55
62
|
type: "task";
|
|
@@ -91,7 +98,19 @@ interface NoteEvent {
|
|
|
91
98
|
};
|
|
92
99
|
}
|
|
93
100
|
|
|
94
|
-
|
|
101
|
+
interface TeachingEvent {
|
|
102
|
+
event: "captured";
|
|
103
|
+
type: "teaching";
|
|
104
|
+
timestamp: string;
|
|
105
|
+
data: {
|
|
106
|
+
domain: string;
|
|
107
|
+
confidence: string;
|
|
108
|
+
text: string;
|
|
109
|
+
source: string;
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
type CaptureEvent = TaskEvent | KnowledgeEvent | NoteEvent | TeachingEvent;
|
|
95
114
|
|
|
96
115
|
function getLogPath(): string {
|
|
97
116
|
const dataHome =
|
|
@@ -209,4 +228,23 @@ export function captureNote(input: NoteInput): CaptureResult {
|
|
|
209
228
|
return writeEvent(event);
|
|
210
229
|
}
|
|
211
230
|
|
|
231
|
+
/**
|
|
232
|
+
* Capture a teaching moment
|
|
233
|
+
*/
|
|
234
|
+
export function captureTeaching(input: TeachingInput): CaptureResult {
|
|
235
|
+
const event: TeachingEvent = {
|
|
236
|
+
event: "captured",
|
|
237
|
+
type: "teaching",
|
|
238
|
+
timestamp: "",
|
|
239
|
+
data: {
|
|
240
|
+
domain: input.domain,
|
|
241
|
+
confidence: input.confidence,
|
|
242
|
+
text: input.text,
|
|
243
|
+
source: input.source || "manual",
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return writeEvent(event);
|
|
248
|
+
}
|
|
249
|
+
|
|
212
250
|
export type { CaptureEvent };
|
package/lib/semantic.ts
CHANGED
|
@@ -223,8 +223,8 @@ export async function semanticSearch(
|
|
|
223
223
|
|
|
224
224
|
const limit = options.limit ?? 20;
|
|
225
225
|
|
|
226
|
-
// KNN query
|
|
227
|
-
//
|
|
226
|
+
// KNN query - 1:1 mapping between search rows and embeddings
|
|
227
|
+
// Content is pre-chunked at ingest time
|
|
228
228
|
let sql: string;
|
|
229
229
|
const params: (Uint8Array | string | number)[] = [queryBlob];
|
|
230
230
|
|
|
@@ -235,17 +235,16 @@ export async function semanticSearch(
|
|
|
235
235
|
s.title,
|
|
236
236
|
s.content,
|
|
237
237
|
s.metadata,
|
|
238
|
-
|
|
238
|
+
e.distance
|
|
239
239
|
FROM embeddings e
|
|
240
240
|
JOIN search s ON e.doc_id = s.rowid
|
|
241
241
|
WHERE e.embedding MATCH ?
|
|
242
242
|
AND k = ?
|
|
243
243
|
AND s.source = ?
|
|
244
|
-
|
|
245
|
-
ORDER BY distance
|
|
244
|
+
ORDER BY e.distance
|
|
246
245
|
LIMIT ?
|
|
247
246
|
`;
|
|
248
|
-
params.push(limit
|
|
247
|
+
params.push(limit);
|
|
249
248
|
params.push(options.source);
|
|
250
249
|
params.push(limit);
|
|
251
250
|
} else {
|
|
@@ -255,16 +254,15 @@ export async function semanticSearch(
|
|
|
255
254
|
s.title,
|
|
256
255
|
s.content,
|
|
257
256
|
s.metadata,
|
|
258
|
-
|
|
257
|
+
e.distance
|
|
259
258
|
FROM embeddings e
|
|
260
259
|
JOIN search s ON e.doc_id = s.rowid
|
|
261
260
|
WHERE e.embedding MATCH ?
|
|
262
261
|
AND k = ?
|
|
263
|
-
|
|
264
|
-
ORDER BY distance
|
|
262
|
+
ORDER BY e.distance
|
|
265
263
|
LIMIT ?
|
|
266
264
|
`;
|
|
267
|
-
params.push(limit
|
|
265
|
+
params.push(limit);
|
|
268
266
|
params.push(limit);
|
|
269
267
|
}
|
|
270
268
|
|