1pub mod session_settings;
70mod store;
71mod types;
72
73pub use session_settings::DEFAULT_KNOWLEDGE_SETTINGS_SESSION_ID;
74pub use session_settings::{KnowledgeSettingsManager, SessionKnowledgeSettings};
75pub use store::{KnowledgeError, KnowledgeStore, register_builtin_knowledge};
76pub use types::{KnowledgeItem, KnowledgeMatch, KnowledgeQuery, KnowledgeReference, LoadCondition};
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use tempfile::tempdir;
82
83 #[test]
84 fn test_store_creation() {
85 let store = KnowledgeStore::with_default_dir();
86 assert_eq!(store.count(), 0);
87 }
88
89 #[test]
90 fn test_register_and_find() {
91 let tmp = tempdir().unwrap();
92 let store = KnowledgeStore::new(tmp.path());
93
94 let item = KnowledgeItem::new("test-item", "Test Item", "A test knowledge item")
95 .with_triggers(["test", "example"]);
96
97 store.register(item);
98 assert_eq!(store.count(), 1);
99
100 let query = KnowledgeQuery {
101 query: "help me with a test".to_string(),
102 ..Default::default()
103 };
104
105 let matches = store.find(&query);
106 assert_eq!(matches.len(), 1);
107 assert_eq!(matches[0].item.id, "test-item");
108 }
109
110 #[test]
111 fn test_builtin_knowledge() {
112 let tmp = tempdir().unwrap();
113 let store = KnowledgeStore::new(tmp.path());
114 register_builtin_knowledge(&store);
115
116 assert!(store.count() > 0);
118
119 let query = KnowledgeQuery {
121 query: "help with rust ownership".to_string(),
122 ..Default::default()
123 };
124 let matches = store.find(&query);
125 assert!(!matches.is_empty());
126 }
127
128 #[test]
129 fn test_builtin_queries_resolve_expected_experts() {
130 let tmp = tempdir().unwrap();
131 let store = KnowledgeStore::new(tmp.path());
132 register_builtin_knowledge(&store);
133
134 for (query, expected_id) in [
135 (
136 "help me fix cargo clippy warnings in async rust with tokio and serde",
137 "rust-expert",
138 ),
139 (
140 "build a tauri plugin command with an invoke handler and capabilities",
141 "tauri-expert",
142 ),
143 (
144 "add a clap subcommand with --json output and no_color-friendly terminal ux",
145 "cli-expert",
146 ),
147 (
148 "transcribe microphone audio with whisper-rs, cpal, vad, and speech-to-text",
149 "voice-expert",
150 ),
151 (
152 "implement an mcp server with tools/list, tools/call, resources/read, and notifications/initialized",
153 "mcp-expert",
154 ),
155 (
156 "publish an agent card for a remote agent and stream task delegation with sendSubscribe",
157 "a2a-expert",
158 ),
159 ] {
160 let matches = store.find(&KnowledgeQuery {
161 query: query.to_string(),
162 limit: Some(3),
163 ..Default::default()
164 });
165
166 assert!(!matches.is_empty(), "expected matches for query: {query}");
167 assert_eq!(matches[0].item.id, expected_id, "query: {query}");
168 assert!(
169 !matches[0].matched_triggers.is_empty(),
170 "expected trigger matches for query: {query}"
171 );
172 }
173 }
174
175 #[test]
176 fn test_specialty_queries_resolve_expected_experts() {
177 let tmp = tempdir().unwrap();
178 let store = KnowledgeStore::new(tmp.path());
179 register_builtin_knowledge(&store);
180
181 for (query, expected_id) in [
182 (
183 "design an analytics metric tree with instrumentation, retention cohorts, and experimentation readouts",
184 "analytics-expert",
185 ),
186 (
187 "review a scientific study for experimental design, evidence quality, reproducibility, and confounders",
188 "science-expert",
189 ),
190 (
191 "work through mathematical modeling, optimization, derivations, and estimation checks",
192 "math-expert",
193 ),
194 (
195 "develop marketing positioning, messaging, go to market planning, and growth campaigns for an ICP",
196 "marketing-expert",
197 ),
198 (
199 "review software system design with api contracts, observability, architecture, and production reliability",
200 "software-systems-expert",
201 ),
202 (
203 "design a robotics autonomy stack covering perception, localization, controls, and robot safety",
204 "robotics-expert",
205 ),
206 (
207 "evaluate a mechanical engineering concept for loads, materials, tolerances, manufacturability, and fatigue",
208 "mechanical-engineering-expert",
209 ),
210 (
211 "review an electrical engineering design for power electronics, pcb interfaces, signal integrity, and validation",
212 "electrical-engineering-expert",
213 ),
214 (
215 "plan a civil engineering site with grading, drainage, structures, constructability, and foundation constraints",
216 "civil-engineering-expert",
217 ),
218 (
219 "analyze a chemical engineering process with mass balance, thermodynamics, reaction kinetics, unit operations, and process safety",
220 "chemical-engineering-expert",
221 ),
222 (
223 "evaluate an aerospace mission architecture with flight dynamics, guidance, control, and verification",
224 "aerospace-expert",
225 ),
226 ] {
227 let matches = store.find(&KnowledgeQuery {
228 query: query.to_string(),
229 limit: Some(3),
230 ..Default::default()
231 });
232
233 assert!(!matches.is_empty(), "expected matches for query: {query}");
234 assert_eq!(matches[0].item.id, expected_id, "query: {query}");
235 assert!(
236 !matches[0].matched_triggers.is_empty(),
237 "expected trigger matches for query: {query}"
238 );
239 }
240 }
241
242 #[test]
243 fn test_categories() {
244 let tmp = tempdir().unwrap();
245 let store = KnowledgeStore::new(tmp.path());
246 register_builtin_knowledge(&store);
247
248 let cats = store.categories();
249 assert!(cats.contains(&"language".to_string()));
250 assert!(cats.contains(&"framework".to_string()));
251 assert!(cats.contains(&"analytics".to_string()));
252 assert!(cats.contains(&"robotics".to_string()));
253 }
254
255 #[test]
256 fn persist_and_reload_user_item() {
257 let tmp = tempdir().unwrap();
258
259 let store = KnowledgeStore::new(tmp.path());
260 register_builtin_knowledge(&store);
261
262 let user_item = KnowledgeItem::new(
263 "my-custom",
264 "My Custom",
265 "Custom knowledge for testing persistence",
266 )
267 .with_triggers(["custom", "persist"])
268 .with_category("user")
269 .with_content("Hello from disk");
270
271 store.upsert_user_item(user_item).unwrap();
272
273 let store2 = KnowledgeStore::new(tmp.path());
275 register_builtin_knowledge(&store2);
276 let loaded = store2.load_user_items().unwrap();
277 assert_eq!(loaded, 1);
278
279 let fetched = store2.get("my-custom").unwrap();
280 assert_eq!(fetched.name, "My Custom");
281 assert!(fetched.core_content.contains("Hello from disk"));
282 assert_eq!(
283 fetched.metadata.get("origin").map(|s| s.as_str()),
284 Some("user")
285 );
286 }
287}