Skip to main content

stynx_code_plugins/infrastructure/
plugin_config.rs

1use std::path::PathBuf;
2
3use serde::Deserialize;
4
5use crate::domain::plugin::{PluginId, PluginInfo, PluginStatus};
6
7#[derive(Debug, Deserialize)]
8pub struct PluginManifest {
9    pub name: String,
10    pub version: String,
11    pub description: String,
12    pub entry_point: String,
13}
14
15pub fn load_plugin_configs() -> Vec<PluginInfo> {
16    let plugins_dir = match plugins_base_dir() {
17        Some(p) => p,
18        None => return vec![],
19    };
20
21    if !plugins_dir.exists() {
22        return vec![];
23    }
24
25    let read_dir = match std::fs::read_dir(&plugins_dir) {
26        Ok(d) => d,
27        Err(_) => return vec![],
28    };
29
30    let mut plugins = Vec::new();
31
32    for entry in read_dir.flatten() {
33        let plugin_dir = entry.path();
34        if !plugin_dir.is_dir() {
35            continue;
36        }
37
38        let manifest_path = plugin_dir.join("plugin.json");
39        if !manifest_path.exists() {
40            continue;
41        }
42
43        let contents = match std::fs::read_to_string(&manifest_path) {
44            Ok(c) => c,
45            Err(_) => continue,
46        };
47
48        let manifest: PluginManifest = match serde_json::from_str(&contents) {
49            Ok(m) => m,
50            Err(_) => continue,
51        };
52
53        let id = entry.file_name().to_string_lossy().to_string();
54        plugins.push(PluginInfo {
55            id: PluginId::new(id),
56            name: manifest.name,
57            version: manifest.version,
58            description: manifest.description,
59            path: plugin_dir,
60            status: PluginStatus::Installed,
61        });
62    }
63
64    plugins
65}
66
67fn plugins_base_dir() -> Option<PathBuf> {
68    stynx_code_config::home_dir()
69        .map(|h| h.join(".claude").join("plugins"))
70}