pub mod manifest;
pub mod loader;
pub mod config;
pub mod registry;
pub mod tool;
pub mod state;
pub mod marketplace;
pub mod plugin_index;
pub mod update_diff;
pub mod install;
pub mod keybinds;
pub mod commands;
pub mod trust;
pub mod post_install;
use std::path::PathBuf;
use std::sync::Arc;
use crate::skills::registry::CommandRegistry;
use crate::skills::tool::LoadSkillTool;
use crate::extensions::manifest::ExtensionManifest;
#[derive(Debug, Clone)]
pub struct Plugin {
pub name: String,
pub root: PathBuf,
pub marketplace: Option<String>,
pub version: Option<String>,
pub description: Option<String>,
pub extension: Option<ExtensionManifest>,
pub manifest: Option<manifest::PluginManifest>,
}
#[derive(Debug, Clone)]
pub struct LoadedSkill {
pub name: String,
pub description: String,
pub body: String, pub plugin: Option<String>, pub base_dir: PathBuf, pub source_path: PathBuf, }
pub const BUILTIN_COMMANDS: &[&str] = &[
"clear", "compact", "chain", "model", "models", "system", "thinking", "sessions",
"resume", "saveas", "theme", "gamba", "help", "quit", "exit",
"settings", "plugins", "extensions", "status", "ping", "keybinds",
"sidecar",
];
pub async fn register(
tools: &Arc<tokio::sync::RwLock<crate::ToolRegistry>>,
config: &crate::SynapsConfig,
) -> (Arc<CommandRegistry>, Arc<std::sync::RwLock<keybinds::KeybindRegistry>>) {
let (mut plugins, mut skills) = loader::load_all(&loader::default_roots());
skills = config::filter_disabled(skills, &config.disabled_plugins, &config.disabled_skills);
if !config.disabled_plugins.is_empty() {
plugins.retain(|p| !config.disabled_plugins.iter().any(|d| d == &p.name));
}
tracing::info!(
plugins = plugins.len(),
skills = skills.len(),
"loaded plugins and skills"
);
let mut kb_registry = keybinds::KeybindRegistry::new();
for plugin in &plugins {
if let Some(ref manifest) = plugin.manifest {
if !manifest.keybinds.is_empty() {
kb_registry.register_plugin(&manifest.name, &manifest.keybinds, &plugin.root);
tracing::info!(
plugin = manifest.name.as_str(),
count = manifest.keybinds.len(),
"registered plugin keybinds"
);
}
}
}
if !config.keybinds.is_empty() {
kb_registry.register_user(&config.keybinds);
}
let sidecar_key = crate::config::read_config_value("sidecar_toggle_key")
.map(|v| v.trim().to_string())
.filter(|v| !v.is_empty())
.unwrap_or_else(|| "F8".to_string());
let mut overrides = std::collections::HashMap::new();
overrides.insert(sidecar_key, "/sidecar toggle".to_string());
kb_registry.register_user(&overrides);
let registry = Arc::new(CommandRegistry::new_with_plugins(BUILTIN_COMMANDS, skills, plugins));
let tool = LoadSkillTool::new(registry.clone());
tools.write().await.register(Arc::new(tool));
(registry, Arc::new(std::sync::RwLock::new(kb_registry)))
}
pub fn reload_registry(registry: &CommandRegistry, config: &crate::SynapsConfig) {
let (mut plugins, mut skills) = loader::load_all(&loader::default_roots());
skills = config::filter_disabled(skills, &config.disabled_plugins, &config.disabled_skills);
if !config.disabled_plugins.is_empty() {
plugins.retain(|p| !config.disabled_plugins.iter().any(|d| d == &p.name));
}
tracing::info!(skills = skills.len(), "reloaded skills");
registry.rebuild_with_plugins(skills, plugins);
}