@voidwire/lore 0.5.5 → 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/cli.ts +86 -29
- package/index.ts +4 -0
- package/lib/capture.ts +138 -59
- package/lib/list.ts +24 -16
- package/lib/projects.ts +2 -1
- package/lib/semantic.ts +28 -55
- package/package.json +1 -1
package/cli.ts
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
captureKnowledge,
|
|
36
36
|
captureNote,
|
|
37
37
|
captureTeaching,
|
|
38
|
+
captureObservation,
|
|
38
39
|
semanticSearch,
|
|
39
40
|
formatBriefSearch,
|
|
40
41
|
hasEmbeddings,
|
|
@@ -48,6 +49,9 @@ import {
|
|
|
48
49
|
type NoteInput,
|
|
49
50
|
type TeachingInput,
|
|
50
51
|
type KnowledgeCaptureType,
|
|
52
|
+
type ObservationInput,
|
|
53
|
+
type ObservationSubtype,
|
|
54
|
+
type ObservationConfidence,
|
|
51
55
|
} from "./index";
|
|
52
56
|
|
|
53
57
|
// ============================================================================
|
|
@@ -521,14 +525,14 @@ Examples:
|
|
|
521
525
|
function handleCaptureTask(args: string[]): void {
|
|
522
526
|
const parsed = parseArgs(args);
|
|
523
527
|
|
|
524
|
-
const required = ["
|
|
528
|
+
const required = ["topic", "name", "problem", "solution"];
|
|
525
529
|
const missing = required.filter((f) => !parsed.has(f));
|
|
526
530
|
if (missing.length > 0) {
|
|
527
531
|
fail(`Missing required fields: ${missing.join(", ")}`);
|
|
528
532
|
}
|
|
529
533
|
|
|
530
534
|
const input: TaskInput = {
|
|
531
|
-
|
|
535
|
+
topic: parsed.get("topic")!,
|
|
532
536
|
name: parsed.get("name")!,
|
|
533
537
|
problem: parsed.get("problem")!,
|
|
534
538
|
solution: parsed.get("solution")!,
|
|
@@ -536,7 +540,7 @@ function handleCaptureTask(args: string[]): void {
|
|
|
536
540
|
discoveries: parseList(parsed.get("discoveries")),
|
|
537
541
|
deviations: parsed.get("deviations"),
|
|
538
542
|
pattern: parsed.get("pattern"),
|
|
539
|
-
|
|
543
|
+
tags: parseList(parsed.get("tags")),
|
|
540
544
|
tech: parseList(parsed.get("tech")),
|
|
541
545
|
difficulty: parsed.get("difficulty"),
|
|
542
546
|
};
|
|
@@ -556,16 +560,16 @@ function handleCaptureTask(args: string[]): void {
|
|
|
556
560
|
function handleCaptureKnowledge(args: string[]): void {
|
|
557
561
|
const parsed = parseArgs(args);
|
|
558
562
|
|
|
559
|
-
const required = ["
|
|
563
|
+
const required = ["topic", "text", "subtype"];
|
|
560
564
|
const missing = required.filter((f) => !parsed.has(f));
|
|
561
565
|
if (missing.length > 0) {
|
|
562
566
|
fail(`Missing required fields: ${missing.join(", ")}`);
|
|
563
567
|
}
|
|
564
568
|
|
|
565
569
|
const input: KnowledgeInput = {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
570
|
+
topic: parsed.get("topic")!,
|
|
571
|
+
content: parsed.get("text")!,
|
|
572
|
+
subtype: parsed.get("subtype")! as KnowledgeCaptureType,
|
|
569
573
|
};
|
|
570
574
|
|
|
571
575
|
const result = captureKnowledge(input);
|
|
@@ -588,9 +592,9 @@ function handleCaptureNote(args: string[]): void {
|
|
|
588
592
|
}
|
|
589
593
|
|
|
590
594
|
const input: NoteInput = {
|
|
591
|
-
|
|
595
|
+
content: parsed.get("text")!,
|
|
592
596
|
tags: parseList(parsed.get("tags")),
|
|
593
|
-
|
|
597
|
+
topic: parsed.get("topic"),
|
|
594
598
|
};
|
|
595
599
|
|
|
596
600
|
const result = captureNote(input);
|
|
@@ -608,16 +612,16 @@ function handleCaptureNote(args: string[]): void {
|
|
|
608
612
|
function handleCaptureTeaching(args: string[]): void {
|
|
609
613
|
const parsed = parseArgs(args);
|
|
610
614
|
|
|
611
|
-
const required = ["
|
|
615
|
+
const required = ["topic", "confidence", "text"];
|
|
612
616
|
const missing = required.filter((f) => !parsed.has(f));
|
|
613
617
|
if (missing.length > 0) {
|
|
614
618
|
fail(`Missing required fields: ${missing.join(", ")}`);
|
|
615
619
|
}
|
|
616
620
|
|
|
617
621
|
const input: TeachingInput = {
|
|
618
|
-
|
|
622
|
+
topic: parsed.get("topic")!,
|
|
619
623
|
confidence: parsed.get("confidence")!,
|
|
620
|
-
|
|
624
|
+
content: parsed.get("text")!,
|
|
621
625
|
source: parsed.get("source"),
|
|
622
626
|
};
|
|
623
627
|
|
|
@@ -633,13 +637,44 @@ function handleCaptureTeaching(args: string[]): void {
|
|
|
633
637
|
}
|
|
634
638
|
}
|
|
635
639
|
|
|
640
|
+
function handleCaptureObservation(args: string[]): void {
|
|
641
|
+
const parsed = parseArgs(args);
|
|
642
|
+
|
|
643
|
+
const required = ["topic", "subtype", "confidence", "text"];
|
|
644
|
+
const missing = required.filter((f) => !parsed.has(f));
|
|
645
|
+
if (missing.length > 0) {
|
|
646
|
+
fail(`Missing required fields: ${missing.join(", ")}`);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const input: ObservationInput = {
|
|
650
|
+
topic: parsed.get("topic")!,
|
|
651
|
+
content: parsed.get("text")!,
|
|
652
|
+
subtype: parsed.get("subtype")! as ObservationSubtype,
|
|
653
|
+
confidence: parsed.get("confidence")! as ObservationConfidence,
|
|
654
|
+
source: parsed.get("source"),
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
const result = captureObservation(input);
|
|
658
|
+
output(result);
|
|
659
|
+
|
|
660
|
+
if (result.success) {
|
|
661
|
+
console.error("✅ Observation logged");
|
|
662
|
+
process.exit(0);
|
|
663
|
+
} else {
|
|
664
|
+
console.error(`❌ ${result.error}`);
|
|
665
|
+
process.exit(2);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
636
669
|
function handleCapture(args: string[]): void {
|
|
637
670
|
if (hasFlag(args, "help")) {
|
|
638
671
|
showCaptureHelp();
|
|
639
672
|
}
|
|
640
673
|
|
|
641
674
|
if (args.length === 0) {
|
|
642
|
-
fail(
|
|
675
|
+
fail(
|
|
676
|
+
"Missing capture type. Use: task, knowledge, note, teaching, or observation",
|
|
677
|
+
);
|
|
643
678
|
}
|
|
644
679
|
|
|
645
680
|
const captureType = args[0];
|
|
@@ -658,9 +693,12 @@ function handleCapture(args: string[]): void {
|
|
|
658
693
|
case "teaching":
|
|
659
694
|
handleCaptureTeaching(captureArgs);
|
|
660
695
|
break;
|
|
696
|
+
case "observation":
|
|
697
|
+
handleCaptureObservation(captureArgs);
|
|
698
|
+
break;
|
|
661
699
|
default:
|
|
662
700
|
fail(
|
|
663
|
-
`Unknown capture type: ${captureType}. Use: task, knowledge, note, or
|
|
701
|
+
`Unknown capture type: ${captureType}. Use: task, knowledge, note, teaching, or observation`,
|
|
664
702
|
);
|
|
665
703
|
}
|
|
666
704
|
}
|
|
@@ -708,34 +746,42 @@ List Options:
|
|
|
708
746
|
|
|
709
747
|
Capture Types:
|
|
710
748
|
task Log task completion
|
|
711
|
-
--
|
|
749
|
+
--topic Project/topic name (required)
|
|
712
750
|
--name Task name (required)
|
|
713
751
|
--problem Problem solved (required)
|
|
714
752
|
--solution Solution pattern (required)
|
|
715
753
|
|
|
716
754
|
knowledge Log insight
|
|
717
|
-
--context
|
|
755
|
+
--topic Topic/context name (required)
|
|
718
756
|
--text Insight text (required)
|
|
719
|
-
--
|
|
757
|
+
--subtype Type: decision, learning, gotcha, preference (required)
|
|
720
758
|
|
|
721
759
|
note Quick note
|
|
722
760
|
--text Note content (required)
|
|
723
761
|
--tags Comma-separated tags
|
|
724
|
-
--
|
|
762
|
+
--topic Optional topic/context
|
|
725
763
|
|
|
726
764
|
teaching Log teaching/learning
|
|
727
|
-
--
|
|
765
|
+
--topic Subject area (required)
|
|
728
766
|
--confidence Certainty level (required)
|
|
729
767
|
--text Teaching content (required)
|
|
730
768
|
--source Optional source identifier
|
|
731
769
|
|
|
770
|
+
observation Log model observation
|
|
771
|
+
--topic Observation topic (required)
|
|
772
|
+
--subtype Type: term, style, pattern, preference, context (required)
|
|
773
|
+
--confidence Level: inferred, stated, verified (required)
|
|
774
|
+
--text Observation content (required)
|
|
775
|
+
--source Optional source identifier
|
|
776
|
+
|
|
732
777
|
Examples:
|
|
733
778
|
lore search "authentication"
|
|
734
779
|
lore search blogs "typescript patterns"
|
|
735
780
|
lore sources
|
|
736
781
|
lore list development
|
|
737
782
|
lore list commits --limit 10 --format human
|
|
738
|
-
lore capture knowledge --
|
|
783
|
+
lore capture knowledge --topic=lore --text="Unified CLI works" --subtype=learning
|
|
784
|
+
lore capture observation --topic=vocabulary --subtype=term --confidence=stated --text="Uses unified schema"
|
|
739
785
|
`);
|
|
740
786
|
process.exit(0);
|
|
741
787
|
}
|
|
@@ -947,12 +993,13 @@ Usage:
|
|
|
947
993
|
lore capture knowledge Log insight/learning
|
|
948
994
|
lore capture note Quick note
|
|
949
995
|
lore capture teaching Log teaching moment
|
|
996
|
+
lore capture observation Log model observation
|
|
950
997
|
|
|
951
998
|
Capture Types:
|
|
952
999
|
|
|
953
1000
|
task - Log completed development task
|
|
954
1001
|
Required:
|
|
955
|
-
--
|
|
1002
|
+
--topic Project/topic name
|
|
956
1003
|
--name Task name
|
|
957
1004
|
--problem Problem solved
|
|
958
1005
|
--solution Solution pattern
|
|
@@ -961,36 +1008,46 @@ Capture Types:
|
|
|
961
1008
|
--discoveries Comma-separated discoveries
|
|
962
1009
|
--deviations Deviation from plan
|
|
963
1010
|
--pattern Pattern name
|
|
964
|
-
--
|
|
1011
|
+
--tags Comma-separated tags
|
|
965
1012
|
--tech Comma-separated technologies
|
|
966
1013
|
--difficulty Difficulty level
|
|
967
1014
|
|
|
968
1015
|
knowledge - Log insight or learning
|
|
969
1016
|
Required:
|
|
970
|
-
--context
|
|
1017
|
+
--topic Topic/context name
|
|
971
1018
|
--text Insight text
|
|
972
|
-
--
|
|
1019
|
+
--subtype Type: decision, learning, gotcha, preference, project, conversation, knowledge
|
|
973
1020
|
|
|
974
1021
|
note - Quick note capture
|
|
975
1022
|
Required:
|
|
976
1023
|
--text Note content
|
|
977
1024
|
Optional:
|
|
978
1025
|
--tags Comma-separated tags
|
|
979
|
-
--
|
|
1026
|
+
--topic Optional topic/context
|
|
980
1027
|
|
|
981
1028
|
teaching - Log teaching or learning moment
|
|
982
1029
|
Required:
|
|
983
|
-
--
|
|
1030
|
+
--topic Subject area (e.g., typescript, architecture)
|
|
984
1031
|
--confidence Certainty level (e.g., high, medium, low)
|
|
985
1032
|
--text Teaching content
|
|
986
1033
|
Optional:
|
|
987
1034
|
--source Source identifier (defaults to "manual")
|
|
988
1035
|
|
|
1036
|
+
observation - Log model observation about user patterns
|
|
1037
|
+
Required:
|
|
1038
|
+
--topic Observation topic
|
|
1039
|
+
--subtype Type: term, style, pattern, preference, context
|
|
1040
|
+
--confidence Level: inferred, stated, verified
|
|
1041
|
+
--text Observation content
|
|
1042
|
+
Optional:
|
|
1043
|
+
--source Source identifier (defaults to "auto")
|
|
1044
|
+
|
|
989
1045
|
Examples:
|
|
990
|
-
lore capture task --
|
|
991
|
-
lore capture knowledge --
|
|
1046
|
+
lore capture task --topic=lore --name="Add help" --problem="No subcommand help" --solution="Added per-command help functions"
|
|
1047
|
+
lore capture knowledge --topic=lore --text="Unified CLI works" --subtype=learning
|
|
992
1048
|
lore capture note --text="Remember to update docs" --tags=docs,todo
|
|
993
|
-
lore capture teaching --
|
|
1049
|
+
lore capture teaching --topic=patterns --confidence=high --text="Prefer composition over inheritance"
|
|
1050
|
+
lore capture observation --topic=vocabulary --subtype=term --confidence=stated --text="Uses 'unified schema'"
|
|
994
1051
|
`);
|
|
995
1052
|
process.exit(0);
|
|
996
1053
|
}
|
package/index.ts
CHANGED
|
@@ -68,6 +68,7 @@ export {
|
|
|
68
68
|
captureTeaching,
|
|
69
69
|
captureInsight,
|
|
70
70
|
captureLearning,
|
|
71
|
+
captureObservation,
|
|
71
72
|
type CaptureResult,
|
|
72
73
|
type KnowledgeInput,
|
|
73
74
|
type KnowledgeCaptureType,
|
|
@@ -77,6 +78,9 @@ export {
|
|
|
77
78
|
type InsightInput,
|
|
78
79
|
type InsightType,
|
|
79
80
|
type LearningInput,
|
|
81
|
+
type ObservationInput,
|
|
82
|
+
type ObservationSubtype,
|
|
83
|
+
type ObservationConfidence,
|
|
80
84
|
type CaptureEvent,
|
|
81
85
|
} from "./lib/capture";
|
|
82
86
|
|
package/lib/capture.ts
CHANGED
|
@@ -25,13 +25,13 @@ export type KnowledgeCaptureType =
|
|
|
25
25
|
| "knowledge";
|
|
26
26
|
|
|
27
27
|
export interface KnowledgeInput {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
topic: string;
|
|
29
|
+
content: string;
|
|
30
|
+
subtype: KnowledgeCaptureType;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export interface TaskInput {
|
|
34
|
-
|
|
34
|
+
topic: string;
|
|
35
35
|
name: string;
|
|
36
36
|
problem: string;
|
|
37
37
|
solution: string;
|
|
@@ -39,21 +39,21 @@ export interface TaskInput {
|
|
|
39
39
|
discoveries?: string[];
|
|
40
40
|
deviations?: string;
|
|
41
41
|
pattern?: string;
|
|
42
|
-
|
|
42
|
+
tags?: string[];
|
|
43
43
|
tech?: string[];
|
|
44
44
|
difficulty?: string;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
export interface NoteInput {
|
|
48
|
-
|
|
48
|
+
content: string;
|
|
49
49
|
tags?: string[];
|
|
50
|
-
|
|
50
|
+
topic?: string;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export interface TeachingInput {
|
|
54
|
-
|
|
54
|
+
topic: string;
|
|
55
55
|
confidence: string;
|
|
56
|
-
|
|
56
|
+
content: string;
|
|
57
57
|
source?: string;
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -67,35 +67,52 @@ export type InsightType =
|
|
|
67
67
|
|
|
68
68
|
export interface InsightInput {
|
|
69
69
|
session_id: string;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
topic: string;
|
|
71
|
+
subtype: InsightType;
|
|
72
|
+
content: string;
|
|
73
73
|
source: "auto";
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
export interface LearningInput {
|
|
77
77
|
topic: string; // "spanish", "guitar", "kubernetes" - the learning topic
|
|
78
78
|
persona: string; // "marcus", "elena", etc.
|
|
79
|
-
|
|
79
|
+
content: string; // "Covered verb conjugations, struggles with subjunctive"
|
|
80
80
|
session_summary?: string; // Longer form session notes
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
export type ObservationSubtype =
|
|
84
|
+
| "term"
|
|
85
|
+
| "style"
|
|
86
|
+
| "pattern"
|
|
87
|
+
| "preference"
|
|
88
|
+
| "context";
|
|
89
|
+
|
|
90
|
+
export type ObservationConfidence = "inferred" | "stated" | "verified";
|
|
91
|
+
|
|
92
|
+
export interface ObservationInput {
|
|
93
|
+
topic: string;
|
|
94
|
+
content: string;
|
|
95
|
+
subtype: ObservationSubtype;
|
|
96
|
+
confidence: ObservationConfidence;
|
|
97
|
+
source?: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
83
100
|
interface TaskEvent {
|
|
84
101
|
event: "captured";
|
|
85
102
|
type: "task";
|
|
86
103
|
timestamp: string;
|
|
87
104
|
data: {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
105
|
+
topic: string;
|
|
106
|
+
name: string;
|
|
107
|
+
problem: string;
|
|
108
|
+
solution: string;
|
|
109
|
+
code?: string;
|
|
93
110
|
discoveries?: string[];
|
|
94
111
|
deviations?: string;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
112
|
+
pattern?: string;
|
|
113
|
+
tags?: string[];
|
|
114
|
+
tech?: string[];
|
|
115
|
+
difficulty?: string;
|
|
99
116
|
};
|
|
100
117
|
}
|
|
101
118
|
|
|
@@ -104,9 +121,9 @@ interface KnowledgeEvent {
|
|
|
104
121
|
type: "knowledge";
|
|
105
122
|
timestamp: string;
|
|
106
123
|
data: {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
124
|
+
topic: string;
|
|
125
|
+
content: string;
|
|
126
|
+
subtype: KnowledgeCaptureType;
|
|
110
127
|
};
|
|
111
128
|
}
|
|
112
129
|
|
|
@@ -117,7 +134,7 @@ interface NoteEvent {
|
|
|
117
134
|
data: {
|
|
118
135
|
content: string;
|
|
119
136
|
tags?: string[];
|
|
120
|
-
|
|
137
|
+
topic?: string;
|
|
121
138
|
};
|
|
122
139
|
}
|
|
123
140
|
|
|
@@ -126,9 +143,9 @@ interface TeachingEvent {
|
|
|
126
143
|
type: "teaching";
|
|
127
144
|
timestamp: string;
|
|
128
145
|
data: {
|
|
129
|
-
|
|
146
|
+
topic: string;
|
|
130
147
|
confidence: string;
|
|
131
|
-
|
|
148
|
+
content: string;
|
|
132
149
|
source: string;
|
|
133
150
|
};
|
|
134
151
|
}
|
|
@@ -139,9 +156,9 @@ interface InsightEvent {
|
|
|
139
156
|
timestamp: string;
|
|
140
157
|
data: {
|
|
141
158
|
session_id: string;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
159
|
+
topic: string;
|
|
160
|
+
subtype: InsightType;
|
|
161
|
+
content: string;
|
|
145
162
|
source: "auto";
|
|
146
163
|
};
|
|
147
164
|
}
|
|
@@ -153,18 +170,32 @@ interface LearningEvent {
|
|
|
153
170
|
data: {
|
|
154
171
|
topic: string; // Learning topic (spanish, guitar, etc.)
|
|
155
172
|
persona: string;
|
|
156
|
-
|
|
173
|
+
content: string;
|
|
157
174
|
session_summary?: string;
|
|
158
175
|
};
|
|
159
176
|
}
|
|
160
177
|
|
|
178
|
+
interface ObservationEvent {
|
|
179
|
+
event: "captured";
|
|
180
|
+
type: "observation";
|
|
181
|
+
timestamp: string;
|
|
182
|
+
data: {
|
|
183
|
+
topic: string;
|
|
184
|
+
content: string;
|
|
185
|
+
subtype: ObservationSubtype;
|
|
186
|
+
confidence: ObservationConfidence;
|
|
187
|
+
source: string;
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
161
191
|
type CaptureEvent =
|
|
162
192
|
| TaskEvent
|
|
163
193
|
| KnowledgeEvent
|
|
164
194
|
| NoteEvent
|
|
165
195
|
| TeachingEvent
|
|
166
196
|
| InsightEvent
|
|
167
|
-
| LearningEvent
|
|
197
|
+
| LearningEvent
|
|
198
|
+
| ObservationEvent;
|
|
168
199
|
|
|
169
200
|
function getLogPath(): string {
|
|
170
201
|
const dataHome =
|
|
@@ -217,10 +248,10 @@ const VALID_KNOWLEDGE_TYPES: KnowledgeCaptureType[] = [
|
|
|
217
248
|
* Capture a knowledge insight
|
|
218
249
|
*/
|
|
219
250
|
export function captureKnowledge(input: KnowledgeInput): CaptureResult {
|
|
220
|
-
if (!VALID_KNOWLEDGE_TYPES.includes(input.
|
|
251
|
+
if (!VALID_KNOWLEDGE_TYPES.includes(input.subtype)) {
|
|
221
252
|
return {
|
|
222
253
|
success: false,
|
|
223
|
-
error: `Invalid
|
|
254
|
+
error: `Invalid subtype: ${input.subtype}. Must be one of: ${VALID_KNOWLEDGE_TYPES.join(", ")}`,
|
|
224
255
|
};
|
|
225
256
|
}
|
|
226
257
|
|
|
@@ -229,9 +260,9 @@ export function captureKnowledge(input: KnowledgeInput): CaptureResult {
|
|
|
229
260
|
type: "knowledge",
|
|
230
261
|
timestamp: "",
|
|
231
262
|
data: {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
263
|
+
topic: input.topic,
|
|
264
|
+
content: input.content,
|
|
265
|
+
subtype: input.subtype,
|
|
235
266
|
},
|
|
236
267
|
};
|
|
237
268
|
|
|
@@ -247,17 +278,17 @@ export function captureTask(input: TaskInput): CaptureResult {
|
|
|
247
278
|
type: "task",
|
|
248
279
|
timestamp: "",
|
|
249
280
|
data: {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
281
|
+
topic: input.topic,
|
|
282
|
+
name: input.name,
|
|
283
|
+
problem: input.problem,
|
|
284
|
+
solution: input.solution,
|
|
285
|
+
code: input.code,
|
|
255
286
|
discoveries: input.discoveries,
|
|
256
287
|
deviations: input.deviations,
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
288
|
+
pattern: input.pattern,
|
|
289
|
+
tags: input.tags,
|
|
290
|
+
tech: input.tech,
|
|
291
|
+
difficulty: input.difficulty,
|
|
261
292
|
},
|
|
262
293
|
};
|
|
263
294
|
|
|
@@ -273,9 +304,9 @@ export function captureNote(input: NoteInput): CaptureResult {
|
|
|
273
304
|
type: "note",
|
|
274
305
|
timestamp: "",
|
|
275
306
|
data: {
|
|
276
|
-
content: input.
|
|
307
|
+
content: input.content,
|
|
277
308
|
tags: input.tags,
|
|
278
|
-
|
|
309
|
+
topic: input.topic,
|
|
279
310
|
},
|
|
280
311
|
};
|
|
281
312
|
|
|
@@ -291,9 +322,9 @@ export function captureTeaching(input: TeachingInput): CaptureResult {
|
|
|
291
322
|
type: "teaching",
|
|
292
323
|
timestamp: "",
|
|
293
324
|
data: {
|
|
294
|
-
|
|
325
|
+
topic: input.topic,
|
|
295
326
|
confidence: input.confidence,
|
|
296
|
-
|
|
327
|
+
content: input.content,
|
|
297
328
|
source: input.source || "manual",
|
|
298
329
|
},
|
|
299
330
|
};
|
|
@@ -314,10 +345,10 @@ const VALID_INSIGHT_TYPES: InsightType[] = [
|
|
|
314
345
|
* Capture an auto-extracted insight from llm-summarize
|
|
315
346
|
*/
|
|
316
347
|
export function captureInsight(input: InsightInput): CaptureResult {
|
|
317
|
-
if (!VALID_INSIGHT_TYPES.includes(input.
|
|
348
|
+
if (!VALID_INSIGHT_TYPES.includes(input.subtype)) {
|
|
318
349
|
return {
|
|
319
350
|
success: false,
|
|
320
|
-
error: `Invalid
|
|
351
|
+
error: `Invalid subtype: ${input.subtype}. Must be one of: ${VALID_INSIGHT_TYPES.join(", ")}`,
|
|
321
352
|
};
|
|
322
353
|
}
|
|
323
354
|
|
|
@@ -327,9 +358,9 @@ export function captureInsight(input: InsightInput): CaptureResult {
|
|
|
327
358
|
timestamp: "",
|
|
328
359
|
data: {
|
|
329
360
|
session_id: input.session_id,
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
361
|
+
topic: input.topic,
|
|
362
|
+
subtype: input.subtype,
|
|
363
|
+
content: input.content,
|
|
333
364
|
source: input.source,
|
|
334
365
|
},
|
|
335
366
|
};
|
|
@@ -341,10 +372,10 @@ export function captureInsight(input: InsightInput): CaptureResult {
|
|
|
341
372
|
* Capture a learning session progress
|
|
342
373
|
*/
|
|
343
374
|
export function captureLearning(input: LearningInput): CaptureResult {
|
|
344
|
-
if (!input.topic || !input.persona || !input.
|
|
375
|
+
if (!input.topic || !input.persona || !input.content) {
|
|
345
376
|
return {
|
|
346
377
|
success: false,
|
|
347
|
-
error: "Missing required fields: topic, persona,
|
|
378
|
+
error: "Missing required fields: topic, persona, content",
|
|
348
379
|
};
|
|
349
380
|
}
|
|
350
381
|
|
|
@@ -355,7 +386,7 @@ export function captureLearning(input: LearningInput): CaptureResult {
|
|
|
355
386
|
data: {
|
|
356
387
|
topic: input.topic,
|
|
357
388
|
persona: input.persona,
|
|
358
|
-
|
|
389
|
+
content: input.content,
|
|
359
390
|
session_summary: input.session_summary,
|
|
360
391
|
},
|
|
361
392
|
};
|
|
@@ -363,4 +394,52 @@ export function captureLearning(input: LearningInput): CaptureResult {
|
|
|
363
394
|
return writeEvent(event);
|
|
364
395
|
}
|
|
365
396
|
|
|
397
|
+
const VALID_OBSERVATION_SUBTYPES: ObservationSubtype[] = [
|
|
398
|
+
"term",
|
|
399
|
+
"style",
|
|
400
|
+
"pattern",
|
|
401
|
+
"preference",
|
|
402
|
+
"context",
|
|
403
|
+
];
|
|
404
|
+
|
|
405
|
+
const VALID_OBSERVATION_CONFIDENCE: ObservationConfidence[] = [
|
|
406
|
+
"inferred",
|
|
407
|
+
"stated",
|
|
408
|
+
"verified",
|
|
409
|
+
];
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Capture a model observation about user patterns
|
|
413
|
+
*/
|
|
414
|
+
export function captureObservation(input: ObservationInput): CaptureResult {
|
|
415
|
+
if (!VALID_OBSERVATION_SUBTYPES.includes(input.subtype)) {
|
|
416
|
+
return {
|
|
417
|
+
success: false,
|
|
418
|
+
error: `Invalid subtype: ${input.subtype}. Must be one of: ${VALID_OBSERVATION_SUBTYPES.join(", ")}`,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (!VALID_OBSERVATION_CONFIDENCE.includes(input.confidence)) {
|
|
423
|
+
return {
|
|
424
|
+
success: false,
|
|
425
|
+
error: `Invalid confidence: ${input.confidence}. Must be one of: ${VALID_OBSERVATION_CONFIDENCE.join(", ")}`,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const event: ObservationEvent = {
|
|
430
|
+
event: "captured",
|
|
431
|
+
type: "observation",
|
|
432
|
+
timestamp: "",
|
|
433
|
+
data: {
|
|
434
|
+
topic: input.topic,
|
|
435
|
+
content: input.content,
|
|
436
|
+
subtype: input.subtype,
|
|
437
|
+
confidence: input.confidence,
|
|
438
|
+
source: input.source || "auto",
|
|
439
|
+
},
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
return writeEvent(event);
|
|
443
|
+
}
|
|
444
|
+
|
|
366
445
|
export type { CaptureEvent };
|
package/lib/list.ts
CHANGED
|
@@ -29,7 +29,8 @@ export type Source =
|
|
|
29
29
|
| "teachings"
|
|
30
30
|
| "sessions"
|
|
31
31
|
| "insights"
|
|
32
|
-
| "learnings"
|
|
32
|
+
| "learnings"
|
|
33
|
+
| "observations";
|
|
33
34
|
|
|
34
35
|
export const SOURCES: Source[] = [
|
|
35
36
|
"development",
|
|
@@ -51,6 +52,7 @@ export const SOURCES: Source[] = [
|
|
|
51
52
|
"sessions",
|
|
52
53
|
"insights",
|
|
53
54
|
"learnings",
|
|
55
|
+
"observations",
|
|
54
56
|
];
|
|
55
57
|
|
|
56
58
|
// Sources that query the 'personal' source with type filter
|
|
@@ -69,10 +71,11 @@ const PROJECT_FIELD: Record<string, string> = {
|
|
|
69
71
|
commits: "project",
|
|
70
72
|
sessions: "project",
|
|
71
73
|
tasks: "project",
|
|
72
|
-
insights: "
|
|
74
|
+
insights: "topic",
|
|
73
75
|
captures: "topic",
|
|
74
76
|
teachings: "topic",
|
|
75
77
|
learnings: "topic",
|
|
78
|
+
observations: "topic",
|
|
76
79
|
};
|
|
77
80
|
|
|
78
81
|
export interface ListOptions {
|
|
@@ -158,23 +161,28 @@ function queryPersonalType(
|
|
|
158
161
|
type: string,
|
|
159
162
|
limit?: number,
|
|
160
163
|
): ListEntry[] {
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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];
|
|
165
172
|
|
|
166
|
-
|
|
167
|
-
|
|
173
|
+
if (limit) {
|
|
174
|
+
sql += " LIMIT ?";
|
|
175
|
+
params.push(limit);
|
|
176
|
+
}
|
|
168
177
|
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
title: row.title,
|
|
172
|
-
content: row.content,
|
|
173
|
-
metadata: JSON.parse(row.metadata || "{}"),
|
|
174
|
-
}))
|
|
175
|
-
.filter((entry) => entry.metadata.type === type);
|
|
178
|
+
const stmt = db.prepare(sql);
|
|
179
|
+
const rows = stmt.all(...params) as RawRow[];
|
|
176
180
|
|
|
177
|
-
return
|
|
181
|
+
return rows.map((row) => ({
|
|
182
|
+
title: row.title,
|
|
183
|
+
content: row.content,
|
|
184
|
+
metadata: JSON.parse(row.metadata || "{}"),
|
|
185
|
+
}));
|
|
178
186
|
}
|
|
179
187
|
|
|
180
188
|
/**
|
package/lib/projects.ts
CHANGED
|
@@ -14,10 +14,11 @@ const PROJECT_FIELD: Record<string, string> = {
|
|
|
14
14
|
commits: "project",
|
|
15
15
|
sessions: "project",
|
|
16
16
|
tasks: "project",
|
|
17
|
-
insights: "
|
|
17
|
+
insights: "topic",
|
|
18
18
|
captures: "topic",
|
|
19
19
|
teachings: "topic",
|
|
20
20
|
learnings: "topic",
|
|
21
|
+
observations: "topic",
|
|
21
22
|
};
|
|
22
23
|
|
|
23
24
|
function getDatabasePath(): string {
|
package/lib/semantic.ts
CHANGED
|
@@ -43,10 +43,11 @@ const PROJECT_FIELD: Record<string, string> = {
|
|
|
43
43
|
commits: "project",
|
|
44
44
|
sessions: "project",
|
|
45
45
|
tasks: "project",
|
|
46
|
-
insights: "
|
|
46
|
+
insights: "topic",
|
|
47
47
|
captures: "topic",
|
|
48
48
|
teachings: "topic",
|
|
49
49
|
learnings: "topic",
|
|
50
|
+
observations: "topic",
|
|
50
51
|
};
|
|
51
52
|
|
|
52
53
|
const MODEL_NAME = "nomic-ai/nomic-embed-text-v1.5";
|
|
@@ -192,70 +193,42 @@ export async function semanticSearch(
|
|
|
192
193
|
|
|
193
194
|
// KNN query - 1:1 mapping between search rows and embeddings
|
|
194
195
|
// Content is pre-chunked at ingest time
|
|
196
|
+
// source/topic partition columns enable filtered KNN (filter BEFORE search)
|
|
195
197
|
let sql: string;
|
|
196
198
|
const params: (Uint8Array | string | number)[] = [queryBlob];
|
|
197
199
|
|
|
200
|
+
// Build KNN query with optional partition filters
|
|
201
|
+
const conditions = ["e.embedding MATCH ?", "k = ?"];
|
|
202
|
+
params.push(limit);
|
|
203
|
+
|
|
198
204
|
if (options.source) {
|
|
199
|
-
|
|
200
|
-
// This filters BEFORE KNN, not after — critical for domain-specific search
|
|
201
|
-
sql = `
|
|
202
|
-
SELECT
|
|
203
|
-
s.source,
|
|
204
|
-
s.title,
|
|
205
|
-
s.content,
|
|
206
|
-
s.metadata,
|
|
207
|
-
e.distance
|
|
208
|
-
FROM embeddings e
|
|
209
|
-
JOIN search s ON e.doc_id = s.rowid
|
|
210
|
-
WHERE e.embedding MATCH ?
|
|
211
|
-
AND k = ?
|
|
212
|
-
AND e.source = ?
|
|
213
|
-
ORDER BY e.distance
|
|
214
|
-
LIMIT ?
|
|
215
|
-
`;
|
|
216
|
-
params.push(limit);
|
|
205
|
+
conditions.push("e.source = ?");
|
|
217
206
|
params.push(options.source);
|
|
218
|
-
params.push(limit);
|
|
219
|
-
} else {
|
|
220
|
-
sql = `
|
|
221
|
-
SELECT
|
|
222
|
-
s.source,
|
|
223
|
-
s.title,
|
|
224
|
-
s.content,
|
|
225
|
-
s.metadata,
|
|
226
|
-
e.distance
|
|
227
|
-
FROM embeddings e
|
|
228
|
-
JOIN search s ON e.doc_id = s.rowid
|
|
229
|
-
WHERE e.embedding MATCH ?
|
|
230
|
-
AND k = ?
|
|
231
|
-
ORDER BY e.distance
|
|
232
|
-
LIMIT ?
|
|
233
|
-
`;
|
|
234
|
-
params.push(limit);
|
|
235
|
-
params.push(limit);
|
|
236
207
|
}
|
|
237
208
|
|
|
238
|
-
const stmt = db.prepare(sql);
|
|
239
|
-
const results = stmt.all(...params) as SemanticResult[];
|
|
240
|
-
|
|
241
|
-
// Post-filter by project if specified
|
|
242
|
-
// KNN WHERE clause doesn't support json_extract on joined metadata,
|
|
243
|
-
// so we filter after the query returns
|
|
244
209
|
if (options.project) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if (!field) return false;
|
|
248
|
-
|
|
249
|
-
try {
|
|
250
|
-
const metadata = JSON.parse(result.metadata);
|
|
251
|
-
return metadata[field] === options.project;
|
|
252
|
-
} catch {
|
|
253
|
-
// Skip results with malformed metadata
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
});
|
|
210
|
+
conditions.push("e.topic = ?");
|
|
211
|
+
params.push(options.project);
|
|
257
212
|
}
|
|
258
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
|
+
|
|
259
232
|
return results;
|
|
260
233
|
} finally {
|
|
261
234
|
db.close();
|