stynx_code_plugins/infrastructure/
plugin_config.rs1use 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}