agent_engine/skills/
mod.rs1pub mod manifest;
12pub mod loader;
13pub mod config;
14pub mod registry;
15pub mod tool;
16pub mod state;
17pub mod marketplace;
18pub mod plugin_index;
19pub mod update_diff;
20pub mod install;
21pub mod keybinds;
22pub mod commands;
23pub mod trust;
24pub mod post_install;
25
26use std::path::PathBuf;
27use std::sync::Arc;
28
29use crate::skills::registry::CommandRegistry;
30use crate::skills::tool::LoadSkillTool;
31use crate::extensions::manifest::ExtensionManifest;
32
33#[derive(Debug, Clone)]
35pub struct Plugin {
36 pub name: String,
37 pub root: PathBuf,
38 pub marketplace: Option<String>,
39 pub version: Option<String>,
40 pub description: Option<String>,
41 pub extension: Option<ExtensionManifest>,
42 pub manifest: Option<manifest::PluginManifest>,
43}
44
45#[derive(Debug, Clone)]
47pub struct LoadedSkill {
48 pub name: String,
49 pub description: String,
50 pub body: String, pub plugin: Option<String>, pub base_dir: PathBuf, pub source_path: PathBuf, }
55
56pub const BUILTIN_COMMANDS: &[&str] = &[
59 "clear", "compact", "chain", "model", "models", "system", "thinking", "sessions",
60 "resume", "saveas", "theme", "gamba", "help", "quit", "exit",
61 "settings", "plugins", "extensions", "status", "stats", "ping", "keybinds",
62 "sidecar",
63];
64
65pub async fn register(
69 tools: &Arc<tokio::sync::RwLock<crate::ToolRegistry>>,
70 config: &crate::SynapsConfig,
71) -> (Arc<CommandRegistry>, Arc<std::sync::RwLock<keybinds::KeybindRegistry>>) {
72 let (mut plugins, mut skills) = tokio::task::spawn_blocking(|| {
76 loader::load_all(&loader::default_roots())
77 })
78 .await
79 .expect("skills::loader::load_all panicked");
80 skills = config::filter_disabled(skills, &config.disabled_plugins, &config.disabled_skills);
81
82 if !config.disabled_plugins.is_empty() {
84 plugins.retain(|p| !config.disabled_plugins.iter().any(|d| d == &p.name));
85 }
86
87 tracing::info!(
88 plugins = plugins.len(),
89 skills = skills.len(),
90 "loaded plugins and skills"
91 );
92
93 let mut kb_registry = keybinds::KeybindRegistry::new();
95 for plugin in &plugins {
96 if let Some(ref manifest) = plugin.manifest {
97 if !manifest.keybinds.is_empty() {
98 kb_registry.register_plugin(&manifest.name, &manifest.keybinds, &plugin.root);
99 tracing::info!(
100 plugin = manifest.name.as_str(),
101 count = manifest.keybinds.len(),
102 "registered plugin keybinds"
103 );
104 }
105 }
106 }
107
108 if !config.keybinds.is_empty() {
110 kb_registry.register_user(&config.keybinds);
111 }
112
113 let sidecar_key = crate::config::read_config_value("sidecar_toggle_key")
119 .map(|v| v.trim().to_string())
120 .filter(|v| !v.is_empty())
121 .unwrap_or_else(|| "F8".to_string());
122 let mut overrides = std::collections::HashMap::new();
123 overrides.insert(sidecar_key, "/sidecar toggle".to_string());
124 kb_registry.register_user(&overrides);
125
126 let registry = Arc::new(CommandRegistry::new_with_plugins(BUILTIN_COMMANDS, skills, plugins));
127 let tool = LoadSkillTool::new(registry.clone());
128 tools.write().await.register(Arc::new(tool));
129 (registry, Arc::new(std::sync::RwLock::new(kb_registry)))
130}
131
132pub fn reload_registry(registry: &CommandRegistry, config: &crate::SynapsConfig) {
135 let (mut plugins, mut skills) = loader::load_all(&loader::default_roots());
136 skills = config::filter_disabled(skills, &config.disabled_plugins, &config.disabled_skills);
137 if !config.disabled_plugins.is_empty() {
138 plugins.retain(|p| !config.disabled_plugins.iter().any(|d| d == &p.name));
139 }
140 tracing::info!(skills = skills.len(), "reloaded skills");
141 registry.rebuild_with_plugins(skills, plugins);
142}