gestura_core_knowledge/
store.rs

1//! Knowledge store for loading and managing knowledge items
2//!
3//! The store handles loading knowledge from the filesystem, caching,
4//! and matching queries to relevant knowledge items.
5
6use super::types::{
7    KnowledgeItem, KnowledgeMatch, KnowledgeQuery, KnowledgeReference, LoadCondition,
8};
9use std::collections::HashMap;
10use std::fs;
11use std::path::{Path, PathBuf};
12use std::sync::RwLock;
13
14/// Error type for knowledge operations
15#[derive(Debug, thiserror::Error)]
16pub enum KnowledgeError {
17    #[error("Knowledge item not found: {0}")]
18    NotFound(String),
19    #[error("Failed to load knowledge: {0}")]
20    LoadError(String),
21    #[error("Failed to parse knowledge file: {0}")]
22    ParseError(String),
23    #[error("Invalid knowledge id: {0}")]
24    InvalidId(String),
25    #[error("Forbidden operation: {0}")]
26    Forbidden(String),
27    #[error("IO error: {0}")]
28    IoError(#[from] std::io::Error),
29}
30
31const ORIGIN_KEY: &str = "origin";
32const ORIGIN_BUILTIN: &str = "builtin";
33const ORIGIN_USER: &str = "user";
34
35/// Knowledge store for managing knowledge items
36pub struct KnowledgeStore {
37    /// All loaded knowledge items
38    items: RwLock<HashMap<String, KnowledgeItem>>,
39    /// Base directory for knowledge files
40    base_dir: PathBuf,
41    /// Cache of loaded reference content
42    reference_cache: RwLock<HashMap<String, String>>,
43}
44
45impl KnowledgeStore {
46    /// Create a new knowledge store with the given base directory
47    pub fn new(base_dir: impl Into<PathBuf>) -> Self {
48        Self {
49            items: RwLock::new(HashMap::new()),
50            base_dir: base_dir.into(),
51            reference_cache: RwLock::new(HashMap::new()),
52        }
53    }
54
55    /// Create a store with default knowledge directory
56    pub fn with_default_dir() -> Self {
57        let base_dir = dirs::config_dir()
58            .unwrap_or_else(|| PathBuf::from("."))
59            .join("gestura")
60            .join("knowledge");
61        Self::new(base_dir)
62    }
63
64    /// Get the base directory
65    pub fn base_dir(&self) -> &Path {
66        &self.base_dir
67    }
68
69    /// Register a knowledge item
70    pub fn register(&self, item: KnowledgeItem) {
71        let mut items = self.items.write().unwrap();
72        items.insert(item.id.clone(), item);
73    }
74
75    fn validate_id(id: &str) -> Result<(), KnowledgeError> {
76        let trimmed = id.trim();
77        if trimmed.is_empty() {
78            return Err(KnowledgeError::InvalidId("id cannot be empty".to_string()));
79        }
80        if trimmed.contains('/') || trimmed.contains('\\') || trimmed.contains("..") {
81            return Err(KnowledgeError::InvalidId(format!(
82                "id contains illegal path characters: {trimmed}"
83            )));
84        }
85        Ok(())
86    }
87
88    fn item_dir(&self, id: &str) -> PathBuf {
89        self.base_dir.join(id)
90    }
91
92    fn item_json_path(&self, id: &str) -> PathBuf {
93        self.item_dir(id).join("item.json")
94    }
95
96    /// Load user-persisted knowledge items from disk.
97    ///
98    /// This is best-effort: invalid entries are skipped (with a warning) so the
99    /// app can still start.
100    pub fn load_user_items(&self) -> Result<usize, KnowledgeError> {
101        fs::create_dir_all(&self.base_dir)?;
102
103        let mut loaded = 0usize;
104        let entries = match fs::read_dir(&self.base_dir) {
105            Ok(e) => e,
106            Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(0),
107            Err(e) => return Err(KnowledgeError::IoError(e)),
108        };
109
110        for entry in entries {
111            let entry = match entry {
112                Ok(e) => e,
113                Err(e) => {
114                    tracing::warn!(error = %e, "Skipping unreadable knowledge dir entry");
115                    continue;
116                }
117            };
118
119            let path = entry.path();
120            if !path.is_dir() {
121                continue;
122            }
123
124            let item_path = path.join("item.json");
125            if !item_path.exists() {
126                continue;
127            }
128
129            let raw = match fs::read_to_string(&item_path) {
130                Ok(s) => s,
131                Err(e) => {
132                    tracing::warn!(path = %item_path.display(), error = %e, "Skipping unreadable knowledge item");
133                    continue;
134                }
135            };
136
137            let mut item: KnowledgeItem = match serde_json::from_str(&raw) {
138                Ok(v) => v,
139                Err(e) => {
140                    tracing::warn!(path = %item_path.display(), error = %e, "Skipping unparseable knowledge item");
141                    continue;
142                }
143            };
144
145            if let Err(e) = Self::validate_id(&item.id) {
146                tracing::warn!(path = %item_path.display(), error = %e, "Skipping knowledge item with invalid id");
147                continue;
148            }
149
150            // Ensure user origin.
151            item.metadata
152                .insert(ORIGIN_KEY.to_string(), ORIGIN_USER.to_string());
153
154            // Do not allow a persisted user item to override a builtin.
155            if let Some(existing) = self.get(&item.id)
156                && existing
157                    .metadata
158                    .get(ORIGIN_KEY)
159                    .is_some_and(|v| v == ORIGIN_BUILTIN)
160            {
161                tracing::warn!(id = %item.id, "Skipping user item that collides with a builtin id");
162                continue;
163            }
164
165            self.register(item);
166            loaded += 1;
167        }
168
169        Ok(loaded)
170    }
171
172    /// Create or update a user knowledge item on disk, and register it in-memory.
173    pub fn upsert_user_item(&self, mut item: KnowledgeItem) -> Result<(), KnowledgeError> {
174        Self::validate_id(&item.id)?;
175
176        // Prevent overwriting builtin items.
177        if let Some(existing) = self.get(&item.id)
178            && existing
179                .metadata
180                .get(ORIGIN_KEY)
181                .is_some_and(|v| v == ORIGIN_BUILTIN)
182        {
183            return Err(KnowledgeError::Forbidden(format!(
184                "cannot modify builtin knowledge item: {}",
185                item.id
186            )));
187        }
188
189        item.metadata
190            .insert(ORIGIN_KEY.to_string(), ORIGIN_USER.to_string());
191
192        let dir = self.item_dir(&item.id);
193        fs::create_dir_all(&dir)?;
194
195        let path = self.item_json_path(&item.id);
196        let tmp_path = path.with_extension("json.tmp");
197        let raw = serde_json::to_string_pretty(&item)
198            .map_err(|e| KnowledgeError::ParseError(e.to_string()))?;
199
200        fs::write(&tmp_path, raw)?;
201        fs::rename(&tmp_path, &path)?;
202
203        self.register(item);
204        Ok(())
205    }
206
207    /// Delete a user knowledge item from disk and remove it from the store.
208    pub fn delete_user_item(&self, id: &str) -> Result<(), KnowledgeError> {
209        Self::validate_id(id)?;
210
211        let existing = self
212            .get(id)
213            .ok_or_else(|| KnowledgeError::NotFound(id.to_string()))?;
214        let origin = existing.metadata.get(ORIGIN_KEY).map(|s| s.as_str());
215        if origin == Some(ORIGIN_BUILTIN) {
216            return Err(KnowledgeError::Forbidden(format!(
217                "cannot delete builtin knowledge item: {id}"
218            )));
219        }
220
221        let dir = self.item_dir(id);
222        if dir.exists() {
223            fs::remove_dir_all(&dir)?;
224        }
225
226        {
227            let mut items = self.items.write().unwrap();
228            items.remove(id);
229        }
230        self.clear_cache();
231        Ok(())
232    }
233
234    /// Register multiple knowledge items
235    pub fn register_all(&self, items: impl IntoIterator<Item = KnowledgeItem>) {
236        let mut store = self.items.write().unwrap();
237        for item in items {
238            store.insert(item.id.clone(), item);
239        }
240    }
241
242    /// Get a knowledge item by ID
243    pub fn get(&self, id: &str) -> Option<KnowledgeItem> {
244        let items = self.items.read().unwrap();
245        items.get(id).cloned()
246    }
247
248    /// List all knowledge items
249    pub fn list(&self) -> Vec<KnowledgeItem> {
250        let items = self.items.read().unwrap();
251        items.values().cloned().collect()
252    }
253
254    /// List knowledge items by category
255    pub fn list_by_category(&self, category: &str) -> Vec<KnowledgeItem> {
256        let items = self.items.read().unwrap();
257        items
258            .values()
259            .filter(|item| item.category == category)
260            .cloned()
261            .collect()
262    }
263
264    /// Find knowledge items matching a query
265    ///
266    /// Searches across all registered items regardless of their `enabled` field.
267    /// Per-session enablement is managed by [`crate::KnowledgeSettingsManager`].
268    pub fn find(&self, query: &KnowledgeQuery) -> Vec<KnowledgeMatch> {
269        let items = self.items.read().unwrap();
270        let mut matches: Vec<KnowledgeMatch> = items
271            .values()
272            .filter(|item| {
273                if let Some(ref cats) = query.categories {
274                    cats.contains(&item.category)
275                } else {
276                    true
277                }
278            })
279            .filter_map(|item| item.matches(&query.query))
280            .filter(|m| m.score >= query.min_score.unwrap_or(0.1))
281            .collect();
282
283        // Sort by score descending, then by priority
284        matches.sort_by(|a, b| {
285            b.score
286                .partial_cmp(&a.score)
287                .unwrap_or(std::cmp::Ordering::Equal)
288                .then_with(|| b.item.priority.cmp(&a.item.priority))
289        });
290
291        if let Some(limit) = query.limit {
292            matches.truncate(limit);
293        }
294
295        matches
296    }
297
298    /// Load a reference file content
299    pub fn load_reference(&self, item_id: &str, ref_id: &str) -> Result<String, KnowledgeError> {
300        let cache_key = format!("{}:{}", item_id, ref_id);
301
302        // Check cache first
303        {
304            let cache = self.reference_cache.read().unwrap();
305            if let Some(content) = cache.get(&cache_key) {
306                return Ok(content.clone());
307            }
308        }
309
310        // Find the reference
311        let items = self.items.read().unwrap();
312        let item = items
313            .get(item_id)
314            .ok_or_else(|| KnowledgeError::NotFound(item_id.to_string()))?;
315
316        let reference = item
317            .references
318            .iter()
319            .find(|r| r.id == ref_id)
320            .ok_or_else(|| KnowledgeError::NotFound(format!("{}:{}", item_id, ref_id)))?;
321
322        // Load from filesystem
323        let ref_path = self.base_dir.join(&item.id).join(&reference.path);
324        let content = std::fs::read_to_string(&ref_path).map_err(|e| {
325            KnowledgeError::LoadError(format!("Failed to load {}: {}", ref_path.display(), e))
326        })?;
327
328        // Cache the content
329        {
330            let mut cache = self.reference_cache.write().unwrap();
331            cache.insert(cache_key, content.clone());
332        }
333
334        Ok(content)
335    }
336
337    /// Get all categories
338    pub fn categories(&self) -> Vec<String> {
339        let items = self.items.read().unwrap();
340        let mut cats: Vec<String> = items.values().map(|i| i.category.clone()).collect();
341        cats.sort();
342        cats.dedup();
343        cats
344    }
345
346    /// Count of knowledge items
347    pub fn count(&self) -> usize {
348        self.items.read().unwrap().len()
349    }
350
351    /// Enable or disable a knowledge item
352    pub fn set_enabled(&self, id: &str, enabled: bool) -> Result<(), KnowledgeError> {
353        let mut items = self.items.write().unwrap();
354        let item = items
355            .get_mut(id)
356            .ok_or_else(|| KnowledgeError::NotFound(id.to_string()))?;
357        item.enabled = enabled;
358        Ok(())
359    }
360
361    /// Clear the reference cache
362    pub fn clear_cache(&self) {
363        let mut cache = self.reference_cache.write().unwrap();
364        cache.clear();
365    }
366}
367
368/// Register built-in knowledge items
369pub fn register_builtin_knowledge(store: &KnowledgeStore) {
370    store.register_all(builtin_knowledge_items());
371}
372
373/// Get built-in knowledge items
374fn builtin_knowledge_items() -> Vec<KnowledgeItem> {
375    let mut items = vec![
376        // Rust Expert
377        KnowledgeItem::new(
378            "rust-expert",
379            "Rust Expert",
380            "Expert Rust programming with Cargo, ownership, async Tokio, Serde, and robust error handling",
381        )
382        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
383        .with_triggers([
384            "rust",
385            "cargo",
386            "cargo fmt",
387            "cargo clippy",
388            "ownership",
389            "borrowing",
390            "lifetime",
391            "serde",
392            "tracing",
393            "thiserror",
394            "anyhow",
395            "async rust",
396            "tokio",
397        ])
398        .with_category("language")
399        .with_content(include_str!("builtin/rust_expert.md"))
400        .with_reference(
401            KnowledgeReference::new("async", "Async Rust", "references/async.md")
402                .with_load_condition(LoadCondition::Keywords(vec![
403                    "async".into(),
404                    "tokio".into(),
405                    "future".into(),
406                ])),
407        )
408        .with_reference(
409            KnowledgeReference::new("error-handling", "Error Handling", "references/errors.md")
410                .with_load_condition(LoadCondition::Keywords(vec![
411                    "error".into(),
412                    "result".into(),
413                    "anyhow".into(),
414                ])),
415        ),
416        // Tauri Expert
417        KnowledgeItem::new(
418            "tauri-expert",
419            "Tauri Expert",
420            "Expert Tauri v2 desktop app development with commands, plugins, capabilities, and frontend/backend IPC",
421        )
422        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
423        .with_triggers([
424            "tauri",
425            "desktop app",
426            "tauri v2",
427            "tauri command",
428            "tauri plugin",
429            "tauri::command",
430            "invoke handler",
431            "capabilities",
432            "webview2",
433            "ipc",
434        ])
435        .with_category("framework")
436        .with_content(include_str!("builtin/tauri_expert.md")),
437        // CLI Development
438        KnowledgeItem::new(
439            "cli-expert",
440            "CLI Expert",
441            "Expert Rust CLI and TUI development with clap, ratatui, structured output, and terminal UX conventions",
442        )
443        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
444        .with_triggers([
445            "cli",
446            "clap",
447            "subcommand",
448            "ratatui",
449            "terminal",
450            "terminal ux",
451            "tui",
452            "command line",
453            "--json",
454            "--dry-run",
455            "no_color",
456            "assert_cmd",
457        ])
458        .with_category("tool")
459        .with_content(include_str!("builtin/cli_expert.md")),
460        // Voice & Audio
461        KnowledgeItem::new(
462            "voice-expert",
463            "Voice Expert",
464            "Expert voice and audio pipelines with Whisper, whisper-rs, cpal, VAD, and speech-to-text",
465        )
466        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
467        .with_triggers([
468            "voice",
469            "whisper",
470            "whisper-rs",
471            "speech",
472            "speech-to-text",
473            "audio",
474            "microphone",
475            "transcription",
476            "stt",
477            "cpal",
478            "vad",
479        ])
480        .with_category("domain")
481        .with_content(include_str!("builtin/voice_expert.md")),
482        // MCP Protocol
483        KnowledgeItem::new(
484            "mcp-expert",
485            "MCP Expert",
486            "Expert Model Context Protocol (2025-11-25) clients, servers, handshake flow, and tool/resource/prompt semantics",
487        )
488        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
489        .with_triggers([
490            "mcp",
491            "model context protocol",
492            "mcp client",
493            "tool calling",
494            "mcp server",
495            "tools/list",
496            "tools/call",
497            "resources/read",
498            "prompts/get",
499            "notifications/initialized",
500            "json-rpc",
501        ])
502        .with_category("protocol")
503        .with_content(include_str!("builtin/mcp_expert.md")),
504        // A2A Protocol
505        KnowledgeItem::new(
506            "a2a-expert",
507            "A2A Expert",
508            "Expert Agent-to-Agent protocol design with Agent Cards, remote-agent delegation, streaming, and authentication",
509        )
510        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
511        .with_triggers([
512            "a2a",
513            "agent to agent",
514            "agent card",
515            "agent protocol",
516            "remote agent",
517            "task delegation",
518            "tasks/send",
519            "sendsubscribe",
520            "bearer auth",
521            "multi-agent",
522        ])
523        .with_category("protocol")
524        .with_content(include_str!("builtin/a2a_expert.md")),
525        // Python Expert
526        KnowledgeItem::new(
527            "python-expert",
528            "Python Expert",
529            "Expert Python programming with modern Python 3.10+ patterns",
530        )
531        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
532        .with_triggers([
533            "python", "pip", "asyncio", "pydantic", "fastapi", "django", "flask", "pytest",
534        ])
535        .with_category("language")
536        .with_content(include_str!("builtin/python_expert.md")),
537        // JavaScript Expert
538        KnowledgeItem::new(
539            "javascript-expert",
540            "JavaScript Expert",
541            "Expert JavaScript programming with modern ES2023+ patterns",
542        )
543        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
544        .with_triggers([
545            "javascript",
546            "js",
547            "node",
548            "nodejs",
549            "npm",
550            "ecmascript",
551            "promise",
552            "event loop",
553        ])
554        .with_category("language")
555        .with_content(include_str!("builtin/javascript_expert.md")),
556        // TypeScript Expert
557        KnowledgeItem::new(
558            "typescript-expert",
559            "TypeScript Expert",
560            "Expert TypeScript programming with strict type system patterns",
561        )
562        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
563        .with_triggers([
564            "typescript",
565            "ts",
566            "tsconfig",
567            "type inference",
568            "generics typescript",
569            "zod",
570        ])
571        .with_category("language")
572        .with_content(include_str!("builtin/typescript_expert.md")),
573        // Go Expert
574        KnowledgeItem::new(
575            "go-expert",
576            "Go Expert",
577            "Expert Go programming with idiomatic patterns and concurrency",
578        )
579        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
580        .with_triggers([
581            "go",
582            "golang",
583            "goroutine",
584            "channel",
585            "go module",
586            "defer",
587            "interface go",
588        ])
589        .with_category("language")
590        .with_content(include_str!("builtin/go_expert.md")),
591        // Java Expert
592        KnowledgeItem::new(
593            "java-expert",
594            "Java Expert",
595            "Expert Java programming with modern Java 21+ and the JVM ecosystem",
596        )
597        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
598        .with_triggers([
599            "java",
600            "jvm",
601            "spring",
602            "maven",
603            "gradle",
604            "record java",
605            "stream api",
606            "virtual threads",
607        ])
608        .with_category("language")
609        .with_content(include_str!("builtin/java_expert.md")),
610        // C++ Expert
611        KnowledgeItem::new(
612            "cpp-expert",
613            "C++ Expert",
614            "Expert C++ programming with modern C++20/23 and RAII patterns",
615        )
616        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
617        .with_triggers([
618            "c++",
619            "cpp",
620            "cmake",
621            "raii",
622            "smart pointer",
623            "template",
624            "stl",
625            "concepts",
626        ])
627        .with_category("language")
628        .with_content(include_str!("builtin/cpp_expert.md")),
629        // C# Expert
630        KnowledgeItem::new(
631            "csharp-expert",
632            "C# Expert",
633            "Expert C# programming with modern C# 12+ and .NET 8+",
634        )
635        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
636        .with_triggers([
637            "c#", "csharp", "dotnet", ".net", "asp.net", "linq", "nuget", "blazor",
638        ])
639        .with_category("language")
640        .with_content(include_str!("builtin/csharp_expert.md")),
641        // Swift Expert
642        KnowledgeItem::new(
643            "swift-expert",
644            "Swift Expert",
645            "Expert Swift programming with Swift 6, concurrency, and Apple platform development",
646        )
647        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
648        .with_triggers([
649            "swift",
650            "swiftui",
651            "ios",
652            "macos development",
653            "xcode",
654            "actor swift",
655            "combine",
656            "swiftpm",
657        ])
658        .with_category("language")
659        .with_content(include_str!("builtin/swift_expert.md")),
660        // Kotlin Expert
661        KnowledgeItem::new(
662            "kotlin-expert",
663            "Kotlin Expert",
664            "Expert Kotlin programming with coroutines and multiplatform development",
665        )
666        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
667        .with_triggers([
668            "kotlin",
669            "coroutines",
670            "ktor",
671            "android kotlin",
672            "kotlin multiplatform",
673            "flow kotlin",
674        ])
675        .with_category("language")
676        .with_content(include_str!("builtin/kotlin_expert.md")),
677        // Ruby Expert
678        KnowledgeItem::new(
679            "ruby-expert",
680            "Ruby Expert",
681            "Expert Ruby programming with Ruby 3.x, Rails, and idiomatic patterns",
682        )
683        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
684        .with_triggers([
685            "ruby",
686            "rails",
687            "gem",
688            "bundler",
689            "rspec",
690            "active record",
691            "rake",
692        ])
693        .with_category("language")
694        .with_content(include_str!("builtin/ruby_expert.md")),
695        // PHP Expert
696        KnowledgeItem::new(
697            "php-expert",
698            "PHP Expert",
699            "Expert PHP programming with modern PHP 8.3+ and Composer ecosystem",
700        )
701        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
702        .with_triggers([
703            "php", "composer", "laravel", "symfony", "phpunit", "psr", "doctrine",
704        ])
705        .with_category("language")
706        .with_content(include_str!("builtin/php_expert.md")),
707    ];
708
709    items.extend(specialty_knowledge_items());
710    items.extend(anthropic_knowledge_work_plugins_sales_items());
711    items.extend(anthropic_skills_items());
712    items
713}
714
715fn specialty_knowledge_items() -> Vec<KnowledgeItem> {
716    vec![
717        specialty_item(
718            "analytics-expert",
719            "Analytics Expert",
720            "Metrics, experimentation, cohorts, and instrumentation for trustworthy product and business analytics",
721            "analytics",
722            [
723                "analytics",
724                "metric tree",
725                "instrumentation",
726                "experimentation",
727                "cohort analysis",
728                "retention",
729            ],
730            include_str!("builtin/analytics_expert.md"),
731        )
732        .with_priority(60),
733        specialty_item(
734            "science-expert",
735            "Science Expert",
736            "Hypotheses, study design, evidence quality, and reproducible scientific reasoning",
737            "science",
738            [
739                "science",
740                "scientific method",
741                "experimental design",
742                "evidence review",
743                "reproducibility",
744                "confounder",
745            ],
746            include_str!("builtin/science_expert.md"),
747        )
748        .with_priority(60),
749        specialty_item(
750            "math-expert",
751            "Math Expert",
752            "Modeling, optimization, derivations, estimation, and quantitative validation",
753            "math",
754            [
755                "math",
756                "mathematical modeling",
757                "optimization",
758                "derivation",
759                "estimation",
760                "proof",
761            ],
762            include_str!("builtin/math_expert.md"),
763        )
764        .with_priority(60),
765        specialty_item(
766            "marketing-expert",
767            "Marketing Expert",
768            "Positioning, messaging, go-to-market planning, campaign design, and growth strategy",
769            "marketing",
770            [
771                "marketing",
772                "positioning",
773                "messaging",
774                "go to market",
775                "campaign strategy",
776                "growth",
777            ],
778            include_str!("builtin/marketing_expert.md"),
779        )
780        .with_priority(60),
781        specialty_item(
782            "software-systems-expert",
783            "Software Systems Expert",
784            "Architecture, interfaces, observability, delivery, and operational reliability for production software systems",
785            "software",
786            [
787                "software systems",
788                "software architecture",
789                "system design",
790                "api contracts",
791                "observability",
792                "production reliability",
793            ],
794            include_str!("builtin/software_systems_expert.md"),
795        )
796        .with_priority(60),
797        specialty_item(
798            "robotics-expert",
799            "Robotics Expert",
800            "Perception, localization, controls, integration, and safety for robotic systems",
801            "robotics",
802            [
803                "robotics",
804                "perception",
805                "localization",
806                "controls",
807                "autonomy",
808                "robot safety",
809            ],
810            include_str!("builtin/robotics_expert.md"),
811        )
812        .with_priority(60),
813        specialty_item(
814            "mechanical-engineering-expert",
815            "Mechanical Engineering Expert",
816            "Loads, materials, tolerances, manufacturability, and reliability in mechanical systems",
817            "mechanical-engineering",
818            [
819                "mechanical engineering",
820                "loads",
821                "materials",
822                "tolerances",
823                "manufacturability",
824                "fatigue",
825            ],
826            include_str!("builtin/mechanical_engineering_expert.md"),
827        )
828        .with_priority(60),
829        specialty_item(
830            "electrical-engineering-expert",
831            "Electrical Engineering Expert",
832            "Power, signals, PCB interfaces, validation, and safe electrical system design",
833            "electrical-engineering",
834            [
835                "electrical engineering",
836                "power electronics",
837                "signal integrity",
838                "pcb",
839                "validation",
840                "sensor interface",
841            ],
842            include_str!("builtin/electrical_engineering_expert.md"),
843        )
844        .with_priority(60),
845        specialty_item(
846            "civil-engineering-expert",
847            "Civil Engineering Expert",
848            "Site design, drainage, structures, grading, and constructability for civil projects",
849            "civil-engineering",
850            [
851                "civil engineering",
852                "site grading",
853                "drainage",
854                "structures",
855                "constructability",
856                "foundation",
857            ],
858            include_str!("builtin/civil_engineering_expert.md"),
859        )
860        .with_priority(60),
861        specialty_item(
862            "chemical-engineering-expert",
863            "Chemical Engineering Expert",
864            "Mass balances, thermodynamics, kinetics, unit operations, and process safety",
865            "chemical-engineering",
866            [
867                "chemical engineering",
868                "mass balance",
869                "thermodynamics",
870                "reaction kinetics",
871                "unit operations",
872                "process safety",
873            ],
874            include_str!("builtin/chemical_engineering_expert.md"),
875        )
876        .with_priority(60),
877        specialty_item(
878            "aerospace-expert",
879            "Aerospace Expert",
880            "Mission architecture, flight dynamics, controls, verification, and aerospace systems reasoning",
881            "aerospace",
882            [
883                "aerospace",
884                "mission architecture",
885                "flight dynamics",
886                "guidance",
887                "control",
888                "verification",
889            ],
890            include_str!("builtin/aerospace_expert.md"),
891        )
892        .with_priority(60),
893        specialty_item(
894            "analytics-metrics-instrumentation",
895            "Analytics: Metrics & Instrumentation",
896            "Metric trees, event design, funnels, and trustworthy instrumentation",
897            "analytics",
898            [
899                "analytics",
900                "metrics",
901                "instrumentation",
902                "dashboard",
903                "funnel",
904                "event taxonomy",
905            ],
906            include_str!("builtin/analytics_metrics_instrumentation.md"),
907        ),
908        specialty_item(
909            "analytics-experimentation-insights",
910            "Analytics: Experimentation & Insights",
911            "Experiment design, readouts, cohort analysis, and decision-oriented insight generation",
912            "analytics",
913            [
914                "ab test",
915                "experiment analysis",
916                "cohort",
917                "segmentation",
918                "insight",
919                "measurement",
920            ],
921            include_str!("builtin/analytics_experimentation_insights.md"),
922        ),
923        specialty_item(
924            "science-hypothesis-design",
925            "Science: Hypothesis & Study Design",
926            "Scientific framing for hypotheses, variables, controls, and study design",
927            "science",
928            [
929                "science",
930                "hypothesis",
931                "study design",
932                "controls",
933                "variables",
934                "research design",
935            ],
936            include_str!("builtin/science_hypothesis_design.md"),
937        ),
938        specialty_item(
939            "science-evidence-review",
940            "Science: Evidence & Review",
941            "Evidence quality, literature review, interpretation, and reproducibility reasoning",
942            "science",
943            [
944                "evidence",
945                "literature review",
946                "reproducibility",
947                "bias",
948                "interpretation",
949                "scientific method",
950            ],
951            include_str!("builtin/science_evidence_review.md"),
952        ),
953        specialty_item(
954            "math-modeling-optimization",
955            "Math: Modeling & Optimization",
956            "Mathematical modeling, optimization setup, constraints, and sensitivity reasoning",
957            "math",
958            [
959                "math",
960                "modeling",
961                "optimization",
962                "constraints",
963                "sensitivity analysis",
964                "objective function",
965            ],
966            include_str!("builtin/math_modeling_optimization.md"),
967        ),
968        specialty_item(
969            "math-derivations-estimation",
970            "Math: Derivations & Estimation",
971            "Stepwise derivations, estimation, dimensional checks, and proof-oriented quantitative work",
972            "math",
973            [
974                "derivation",
975                "estimation",
976                "proof",
977                "units",
978                "order of magnitude",
979                "quantitative reasoning",
980            ],
981            include_str!("builtin/math_derivations_estimation.md"),
982        ),
983        specialty_item(
984            "marketing-positioning-messaging",
985            "Marketing: Positioning & Messaging",
986            "Audience definition, positioning, proof points, and message architecture",
987            "marketing",
988            [
989                "marketing",
990                "positioning",
991                "messaging",
992                "brand",
993                "icp",
994                "value proposition",
995            ],
996            include_str!("builtin/marketing_positioning_messaging.md"),
997        ),
998        specialty_item(
999            "marketing-campaign-growth",
1000            "Marketing: Campaigns & Growth",
1001            "Channel planning, launches, funnel experiments, and growth execution",
1002            "marketing",
1003            [
1004                "campaign",
1005                "growth",
1006                "launch",
1007                "content strategy",
1008                "channel mix",
1009                "go to market",
1010            ],
1011            include_str!("builtin/marketing_campaign_growth.md"),
1012        ),
1013        specialty_item(
1014            "software-architecture-interfaces",
1015            "Software: Architecture & Interfaces",
1016            "System boundaries, APIs, contracts, and production software structure",
1017            "software",
1018            [
1019                "software architecture",
1020                "system design",
1021                "api design",
1022                "interfaces",
1023                "data flow",
1024                "service boundaries",
1025            ],
1026            include_str!("builtin/software_architecture_interfaces.md"),
1027        ),
1028        specialty_item(
1029            "software-reliability-delivery",
1030            "Software: Reliability & Delivery",
1031            "Observability, rollout planning, test strategy, and operational reliability",
1032            "software",
1033            [
1034                "reliability",
1035                "observability",
1036                "testing strategy",
1037                "rollout",
1038                "incident prevention",
1039                "delivery",
1040            ],
1041            include_str!("builtin/software_reliability_delivery.md"),
1042        ),
1043        specialty_item(
1044            "robotics-perception-integration",
1045            "Robotics: Perception & Integration",
1046            "Sensors, localization, integration, calibration, and hardware/software interfaces",
1047            "robotics",
1048            [
1049                "robotics",
1050                "perception",
1051                "sensor fusion",
1052                "localization",
1053                "integration",
1054                "calibration",
1055            ],
1056            include_str!("builtin/robotics_perception_integration.md"),
1057        ),
1058        specialty_item(
1059            "robotics-controls-safety",
1060            "Robotics: Controls & Safety",
1061            "Planning, controls, fallback modes, latency, and safe robot behavior",
1062            "robotics",
1063            [
1064                "controls",
1065                "path planning",
1066                "safety envelope",
1067                "latency",
1068                "autonomy",
1069                "hardware in the loop",
1070            ],
1071            include_str!("builtin/robotics_controls_safety.md"),
1072        ),
1073        specialty_item(
1074            "mechanical-design-materials",
1075            "Mechanical Engineering: Design & Materials",
1076            "Loads, geometry, material choice, tolerances, and mechanical concept selection",
1077            "mechanical-engineering",
1078            [
1079                "mechanical engineering",
1080                "mechanical design",
1081                "materials",
1082                "loads",
1083                "tolerance",
1084                "stress",
1085            ],
1086            include_str!("builtin/mechanical_design_materials.md"),
1087        ),
1088        specialty_item(
1089            "mechanical-manufacturing-reliability",
1090            "Mechanical Engineering: Manufacturing & Reliability",
1091            "Manufacturability, fatigue, wear, maintenance, and failure-mode reasoning",
1092            "mechanical-engineering",
1093            [
1094                "manufacturability",
1095                "fatigue",
1096                "wear",
1097                "assembly",
1098                "maintenance",
1099                "failure modes",
1100            ],
1101            include_str!("builtin/mechanical_manufacturing_reliability.md"),
1102        ),
1103        specialty_item(
1104            "electrical-power-interfaces",
1105            "Electrical Engineering: Power & Interfaces",
1106            "Power architecture, electrical interfaces, grounding, and block-level partitioning",
1107            "electrical-engineering",
1108            [
1109                "electrical engineering",
1110                "power electronics",
1111                "interfaces",
1112                "grounding",
1113                "power budget",
1114                "block diagram",
1115            ],
1116            include_str!("builtin/electrical_power_interfaces.md"),
1117        ),
1118        specialty_item(
1119            "electrical-signals-validation",
1120            "Electrical Engineering: Signals & Validation",
1121            "Signal integrity, bring-up, measurement planning, and hardware validation",
1122            "electrical-engineering",
1123            [
1124                "signal integrity",
1125                "analog",
1126                "digital design",
1127                "pcb",
1128                "bring up",
1129                "hardware validation",
1130            ],
1131            include_str!("builtin/electrical_signals_validation.md"),
1132        ),
1133        specialty_item(
1134            "civil-site-drainage",
1135            "Civil Engineering: Site & Drainage",
1136            "Site constraints, grading, water movement, and civil layout reasoning",
1137            "civil-engineering",
1138            [
1139                "civil engineering",
1140                "site design",
1141                "drainage",
1142                "grading",
1143                "stormwater",
1144                "site constraints",
1145            ],
1146            include_str!("builtin/civil_site_drainage.md"),
1147        ),
1148        specialty_item(
1149            "civil-structures-constructability",
1150            "Civil Engineering: Structures & Constructability",
1151            "Load paths, constructability, sequencing, and code-aware infrastructure reasoning",
1152            "civil-engineering",
1153            [
1154                "structural load",
1155                "constructability",
1156                "sequencing",
1157                "code review",
1158                "geotechnical",
1159                "infrastructure",
1160            ],
1161            include_str!("builtin/civil_structures_constructability.md"),
1162        ),
1163        specialty_item(
1164            "chemical-balances-thermodynamics",
1165            "Chemical Engineering: Balances & Thermodynamics",
1166            "Mass/energy balances, operating windows, and thermodynamic process reasoning",
1167            "chemical-engineering",
1168            [
1169                "chemical engineering",
1170                "mass balance",
1171                "energy balance",
1172                "thermodynamics",
1173                "operating window",
1174                "process model",
1175            ],
1176            include_str!("builtin/chemical_balances_thermodynamics.md"),
1177        ),
1178        specialty_item(
1179            "chemical-kinetics-process-safety",
1180            "Chemical Engineering: Kinetics & Process Safety",
1181            "Reaction rates, separations, hazards, controllability, and process safety",
1182            "chemical-engineering",
1183            [
1184                "reaction kinetics",
1185                "separation process",
1186                "process safety",
1187                "hazard review",
1188                "controllability",
1189                "scale up",
1190            ],
1191            include_str!("builtin/chemical_kinetics_process_safety.md"),
1192        ),
1193        specialty_item(
1194            "aerospace-mission-architecture",
1195            "Aerospace: Mission & Architecture",
1196            "Mission profile, subsystem partitioning, budgets, and aerospace system tradeoffs",
1197            "aerospace",
1198            [
1199                "aerospace",
1200                "mission profile",
1201                "mass budget",
1202                "flight systems",
1203                "subsystems",
1204                "trade study",
1205            ],
1206            include_str!("builtin/aerospace_mission_architecture.md"),
1207        ),
1208        specialty_item(
1209            "aerospace-guidance-verification",
1210            "Aerospace: Guidance & Verification",
1211            "GNC, verification planning, safety margins, and qualification-oriented review",
1212            "aerospace",
1213            [
1214                "gnc",
1215                "verification",
1216                "qualification",
1217                "flight safety",
1218                "guidance",
1219                "avionics validation",
1220            ],
1221            include_str!("builtin/aerospace_guidance_verification.md"),
1222        ),
1223    ]
1224}
1225
1226fn specialty_item<const N: usize>(
1227    id: &'static str,
1228    name: &'static str,
1229    description: &'static str,
1230    category: &'static str,
1231    triggers: [&'static str; N],
1232    content: &'static str,
1233) -> KnowledgeItem {
1234    KnowledgeItem::new(id, name, description)
1235        .with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
1236        .with_triggers(triggers)
1237        .with_category(category)
1238        .with_content(content)
1239}
1240
1241fn with_apache_source(
1242    item: KnowledgeItem,
1243    source_repo: &'static str,
1244    source_path: &'static str,
1245    source_url: &'static str,
1246) -> KnowledgeItem {
1247    item.with_metadata(ORIGIN_KEY, ORIGIN_BUILTIN)
1248        .with_metadata("license", "Apache-2.0")
1249        .with_metadata("source_repo", source_repo)
1250        .with_metadata("source_path", source_path)
1251        .with_metadata("source_url", source_url)
1252}
1253
1254fn anthropic_knowledge_work_plugins_sales_items() -> Vec<KnowledgeItem> {
1255    const REPO: &str = "anthropics/knowledge-work-plugins";
1256
1257    vec![
1258        // Commands (slash-command style playbooks)
1259        with_apache_source(
1260            KnowledgeItem::new(
1261                "sales-call-summary",
1262                "Sales: Call Summary",
1263                "Turn call notes/transcripts into action items + follow-up email",
1264            )
1265            .with_triggers([
1266                "sales",
1267                "call summary",
1268                "meeting notes",
1269                "transcript",
1270                "follow-up email",
1271                "action items",
1272            ])
1273            .with_category("sales")
1274            .with_priority(60)
1275            .with_content(include_str!("builtin/anthropics/sales_call_summary.md")),
1276            REPO,
1277            "sales/commands/call-summary.md",
1278            "https://raw.githubusercontent.com/anthropics/knowledge-work-plugins/main/sales/commands/call-summary.md",
1279        ),
1280        with_apache_source(
1281            KnowledgeItem::new(
1282                "sales-forecast",
1283                "Sales: Forecast",
1284                "Generate commit/upside forecast with best/likely/worst scenarios",
1285            )
1286            .with_triggers([
1287                "sales",
1288                "forecast",
1289                "quota",
1290                "pipeline",
1291                "commit",
1292                "upside",
1293                "coverage ratio",
1294            ])
1295            .with_category("sales")
1296            .with_priority(55)
1297            .with_content(include_str!("builtin/anthropics/sales_forecast.md")),
1298            REPO,
1299            "sales/commands/forecast.md",
1300            "https://raw.githubusercontent.com/anthropics/knowledge-work-plugins/main/sales/commands/forecast.md",
1301        ),
1302        with_apache_source(
1303            KnowledgeItem::new(
1304                "sales-pipeline-review",
1305                "Sales: Pipeline Review",
1306                "Pipeline health check + prioritization + weekly action plan",
1307            )
1308            .with_triggers([
1309                "sales",
1310                "pipeline review",
1311                "pipeline health",
1312                "stale deals",
1313                "close date",
1314                "next steps",
1315            ])
1316            .with_category("sales")
1317            .with_priority(55)
1318            .with_content(include_str!("builtin/anthropics/sales_pipeline_review.md")),
1319            REPO,
1320            "sales/commands/pipeline-review.md",
1321            "https://raw.githubusercontent.com/anthropics/knowledge-work-plugins/main/sales/commands/pipeline-review.md",
1322        ),
1323        // Skills (auto-triggered workflows)
1324        with_apache_source(
1325            KnowledgeItem::new(
1326                "sales-account-research",
1327                "Sales: Account Research",
1328                "Research a company/person for outreach and meeting prep",
1329            )
1330            .with_triggers([
1331                "account research",
1332                "prospect research",
1333                "company research",
1334                "firmographics",
1335                "linkedin",
1336                "sales",
1337            ])
1338            .with_category("sales")
1339            .with_priority(50)
1340            .with_content(include_str!("builtin/anthropics/sales_account_research.md")),
1341            REPO,
1342            "sales/skills/account-research/SKILL.md",
1343            "https://raw.githubusercontent.com/anthropics/knowledge-work-plugins/main/sales/skills/account-research/SKILL.md",
1344        ),
1345        with_apache_source(
1346            KnowledgeItem::new(
1347                "sales-call-prep",
1348                "Sales: Call Prep",
1349                "Generate a call brief (agenda, questions, objections) for a prospect meeting",
1350            )
1351            .with_triggers([
1352                "call prep",
1353                "meeting prep",
1354                "discovery call",
1355                "demo",
1356                "agenda",
1357                "sales",
1358            ])
1359            .with_category("sales")
1360            .with_priority(50)
1361            .with_content(include_str!("builtin/anthropics/sales_call_prep.md")),
1362            REPO,
1363            "sales/skills/call-prep/SKILL.md",
1364            "https://raw.githubusercontent.com/anthropics/knowledge-work-plugins/main/sales/skills/call-prep/SKILL.md",
1365        ),
1366        with_apache_source(
1367            KnowledgeItem::new(
1368                "sales-competitive-intelligence",
1369                "Sales: Competitive Intelligence",
1370                "Create a competitor battlecard + positioning talk-tracks",
1371            )
1372            .with_triggers([
1373                "competitive intelligence",
1374                "battlecard",
1375                "competitor",
1376                "positioning",
1377                "win loss",
1378                "sales",
1379            ])
1380            .with_category("sales")
1381            .with_priority(45)
1382            .with_content(include_str!(
1383                "builtin/anthropics/sales_competitive_intelligence.md"
1384            )),
1385            REPO,
1386            "sales/skills/competitive-intelligence/SKILL.md",
1387            "https://raw.githubusercontent.com/anthropics/knowledge-work-plugins/main/sales/skills/competitive-intelligence/SKILL.md",
1388        ),
1389        with_apache_source(
1390            KnowledgeItem::new(
1391                "sales-create-an-asset",
1392                "Sales: Create an Asset",
1393                "Build customer-ready sales assets (one-pagers, decks, landing pages, workflows)",
1394            )
1395            .with_triggers([
1396                "create an asset",
1397                "sales asset",
1398                "one pager",
1399                "deck",
1400                "landing page",
1401                "workflow demo",
1402            ])
1403            .with_category("sales")
1404            .with_priority(45)
1405            .with_content(include_str!("builtin/anthropics/sales_create_an_asset.md")),
1406            REPO,
1407            "sales/skills/create-an-asset/SKILL.md",
1408            "https://raw.githubusercontent.com/anthropics/knowledge-work-plugins/main/sales/skills/create-an-asset/SKILL.md",
1409        ),
1410        with_apache_source(
1411            KnowledgeItem::new(
1412                "sales-daily-briefing",
1413                "Sales: Daily Briefing",
1414                "Daily prioritized plan (meetings, pipeline alerts, email priorities)",
1415            )
1416            .with_triggers([
1417                "daily briefing",
1418                "morning brief",
1419                "what should i do today",
1420                "sales",
1421                "pipeline alerts",
1422            ])
1423            .with_category("sales")
1424            .with_priority(40)
1425            .with_content(include_str!("builtin/anthropics/sales_daily_briefing.md")),
1426            REPO,
1427            "sales/skills/daily-briefing/SKILL.md",
1428            "https://raw.githubusercontent.com/anthropics/knowledge-work-plugins/main/sales/skills/daily-briefing/SKILL.md",
1429        ),
1430        with_apache_source(
1431            KnowledgeItem::new(
1432                "sales-draft-outreach",
1433                "Sales: Draft Outreach",
1434                "Research-first cold outreach (email + LinkedIn) with clear CTA",
1435            )
1436            .with_triggers([
1437                "draft outreach",
1438                "cold email",
1439                "linkedin message",
1440                "personalized outreach",
1441                "sales",
1442            ])
1443            .with_category("sales")
1444            .with_priority(45)
1445            .with_content(include_str!("builtin/anthropics/sales_draft_outreach.md")),
1446            REPO,
1447            "sales/skills/draft-outreach/SKILL.md",
1448            "https://raw.githubusercontent.com/anthropics/knowledge-work-plugins/main/sales/skills/draft-outreach/SKILL.md",
1449        ),
1450    ]
1451}
1452
1453fn anthropic_skills_items() -> Vec<KnowledgeItem> {
1454    const REPO: &str = "anthropics/skills";
1455
1456    vec![
1457        with_apache_source(
1458            KnowledgeItem::new(
1459                "anthropic-mcp-builder",
1460                "MCP Builder (Anthropic Skill)",
1461                "Guide to building high-quality MCP servers and tools",
1462            )
1463            .with_triggers([
1464                "mcp",
1465                "model context protocol",
1466                "mcp server",
1467                "tool schema",
1468                "tool design",
1469                "inspector",
1470            ])
1471            .with_category("protocol")
1472            .with_priority(55)
1473            .with_content(include_str!("builtin/anthropics/skill_mcp_builder.md")),
1474            REPO,
1475            "skills/mcp-builder/SKILL.md",
1476            "https://raw.githubusercontent.com/anthropics/skills/main/skills/mcp-builder/SKILL.md",
1477        ),
1478        with_apache_source(
1479            KnowledgeItem::new(
1480                "anthropic-frontend-design",
1481                "Frontend Design (Anthropic Skill)",
1482                "Guidelines for distinctive, production-grade UI implementation",
1483            )
1484            .with_triggers([
1485                "frontend design",
1486                "ui design",
1487                "css",
1488                "react",
1489                "component",
1490                "visual design",
1491                "typography",
1492            ])
1493            .with_category("design")
1494            .with_priority(50)
1495            .with_content(include_str!("builtin/anthropics/skill_frontend_design.md")),
1496            REPO,
1497            "skills/frontend-design/SKILL.md",
1498            "https://raw.githubusercontent.com/anthropics/skills/main/skills/frontend-design/SKILL.md",
1499        ),
1500    ]
1501}
1502
1503#[cfg(test)]
1504mod tests {
1505    use super::builtin_knowledge_items;
1506
1507    #[test]
1508    fn specialty_knowledge_items_are_registered() {
1509        let ids = builtin_knowledge_items()
1510            .into_iter()
1511            .map(|item| item.id)
1512            .collect::<std::collections::HashSet<_>>();
1513
1514        for expected_id in [
1515            "analytics-expert",
1516            "analytics-metrics-instrumentation",
1517            "analytics-experimentation-insights",
1518            "science-expert",
1519            "science-hypothesis-design",
1520            "science-evidence-review",
1521            "math-expert",
1522            "math-modeling-optimization",
1523            "math-derivations-estimation",
1524            "marketing-expert",
1525            "marketing-positioning-messaging",
1526            "marketing-campaign-growth",
1527            "software-systems-expert",
1528            "software-architecture-interfaces",
1529            "software-reliability-delivery",
1530            "robotics-expert",
1531            "robotics-perception-integration",
1532            "robotics-controls-safety",
1533            "mechanical-engineering-expert",
1534            "mechanical-design-materials",
1535            "mechanical-manufacturing-reliability",
1536            "electrical-engineering-expert",
1537            "electrical-power-interfaces",
1538            "electrical-signals-validation",
1539            "civil-engineering-expert",
1540            "civil-site-drainage",
1541            "civil-structures-constructability",
1542            "chemical-engineering-expert",
1543            "chemical-balances-thermodynamics",
1544            "chemical-kinetics-process-safety",
1545            "aerospace-expert",
1546            "aerospace-mission-architecture",
1547            "aerospace-guidance-verification",
1548        ] {
1549            assert!(ids.contains(expected_id), "missing {expected_id}");
1550        }
1551    }
1552}