1pub 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", "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) = loader::load_all(&loader::default_roots());
73 skills = config::filter_disabled(skills, &config.disabled_plugins, &config.disabled_skills);
74
75 if !config.disabled_plugins.is_empty() {
77 plugins.retain(|p| !config.disabled_plugins.iter().any(|d| d == &p.name));
78 }
79
80 tracing::info!(
81 plugins = plugins.len(),
82 skills = skills.len(),
83 "loaded plugins and skills"
84 );
85
86 let mut kb_registry = keybinds::KeybindRegistry::new();
88 for plugin in &plugins {
89 if let Some(ref manifest) = plugin.manifest {
90 if !manifest.keybinds.is_empty() {
91 kb_registry.register_plugin(&manifest.name, &manifest.keybinds, &plugin.root);
92 tracing::info!(
93 plugin = manifest.name.as_str(),
94 count = manifest.keybinds.len(),
95 "registered plugin keybinds"
96 );
97 }
98 }
99 }
100
101 if !config.keybinds.is_empty() {
103 kb_registry.register_user(&config.keybinds);
104 }
105
106 let sidecar_key = crate::config::read_config_value("sidecar_toggle_key")
112 .map(|v| v.trim().to_string())
113 .filter(|v| !v.is_empty())
114 .unwrap_or_else(|| "F8".to_string());
115 let mut overrides = std::collections::HashMap::new();
116 overrides.insert(sidecar_key, "/sidecar toggle".to_string());
117 kb_registry.register_user(&overrides);
118
119 let registry = Arc::new(CommandRegistry::new_with_plugins(BUILTIN_COMMANDS, skills, plugins));
120 let tool = LoadSkillTool::new(registry.clone());
121 tools.write().await.register(Arc::new(tool));
122 (registry, Arc::new(std::sync::RwLock::new(kb_registry)))
123}
124
125pub fn reload_registry(registry: &CommandRegistry, config: &crate::SynapsConfig) {
128 let (mut plugins, mut skills) = loader::load_all(&loader::default_roots());
129 skills = config::filter_disabled(skills, &config.disabled_plugins, &config.disabled_skills);
130 if !config.disabled_plugins.is_empty() {
131 plugins.retain(|p| !config.disabled_plugins.iter().any(|d| d == &p.name));
132 }
133 tracing::info!(skills = skills.len(), "reloaded skills");
134 registry.rebuild_with_plugins(skills, plugins);
135}