codewhale-tui 0.8.66

Terminal UI for open-source and open-weight coding models
use std::path::{Path, PathBuf};

use super::manifest::{LoadedPlugin, PluginManifest};
use super::registry::PluginRegistry;

const PLUGIN_MANIFEST: &str = "plugin.toml";

pub fn default_user_plugins_dir() -> PathBuf {
    dirs::home_dir()
        .map(|p| p.join(".codewhale").join("plugins"))
        .unwrap_or_else(|| PathBuf::from("/tmp/codewhale/plugins"))
}

pub fn discover_all(builtin_dirs: &[&str]) -> PluginRegistry {
    let mut registry = PluginRegistry::new();

    for dir in builtin_dirs {
        let path = PathBuf::from(dir);
        if path.exists() {
            discover_from_dir(&path, &mut registry, true);
        }
    }

    let user_dir = default_user_plugins_dir();
    if user_dir.exists() {
        discover_from_dir(&user_dir, &mut registry, false);
    }

    registry
}

fn discover_from_dir(dir: &Path, registry: &mut PluginRegistry, builtin: bool) {
    let Ok(entries) = std::fs::read_dir(dir) else {
        return;
    };

    for entry in entries.flatten() {
        let path = entry.path();
        if !path.is_dir() {
            continue;
        }

        let manifest_path = path.join(PLUGIN_MANIFEST);
        if !manifest_path.exists() {
            continue;
        }

        match PluginManifest::from_path(&manifest_path) {
            Ok(manifest) => {
                if !manifest.check_when() {
                    continue;
                }

                let name = manifest.plugin.name.clone();
                let plugin = LoadedPlugin {
                    manifest,
                    base_path: path,
                    enabled: !builtin,
                };

                registry.register(name, plugin);
            }
            Err(e) => {
                tracing::warn!("Failed to load plugin from {}: {}", path.display(), e);
            }
        }
    }
}