use std::collections::BTreeSet;
use std::path::PathBuf;
pub fn enabled_plugins() -> Vec<String> {
let home = match dirs::home_dir() { Some(h) => h, None => return Vec::new() };
let settings_path: PathBuf = home.join(".claude").join("settings.json");
let installed_path: PathBuf = home.join(".claude").join("plugins").join("installed_plugins.json");
let enabled: BTreeSet<String> = match std::fs::read_to_string(&settings_path) {
Ok(s) => parse_enabled(&s),
Err(_) => BTreeSet::new(),
};
let installed: BTreeSet<String> = match std::fs::read_to_string(&installed_path) {
Ok(s) => parse_installed(&s),
Err(_) => BTreeSet::new(),
};
let mut out: BTreeSet<String> = BTreeSet::new();
for full in enabled.intersection(&installed) {
let display = full.split('@').next().unwrap_or(full).to_string();
let clean = crate::format::sanitize_control(&display);
if !clean.is_empty() { out.insert(clean); }
}
out.into_iter().collect()
}
fn parse_enabled(text: &str) -> BTreeSet<String> {
let mut out = BTreeSet::new();
let v: serde_json::Value = match serde_json::from_str(text) {
Ok(v) => v, Err(_) => return out,
};
let map = match v.get("enabledPlugins").and_then(|x| x.as_object()) {
Some(m) => m, None => return out,
};
for (k, val) in map {
if val.as_bool() == Some(true) {
out.insert(k.to_string());
}
}
out
}
fn parse_installed(text: &str) -> BTreeSet<String> {
let mut out = BTreeSet::new();
let v: serde_json::Value = match serde_json::from_str(text) {
Ok(v) => v, Err(_) => return out,
};
let map = match v.get("plugins").and_then(|x| x.as_object()) {
Some(m) => m, None => return out,
};
for k in map.keys() {
out.insert(k.to_string());
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_enabled_map() {
let s = r#"{"enabledPlugins":{"caveman@caveman":true,"old@m":false}}"#;
let got = parse_enabled(s);
assert!(got.contains("caveman@caveman"));
assert!(!got.contains("old@m"));
}
#[test]
fn parses_installed_map() {
let s = r#"{"version":2,"plugins":{"caveman@caveman":[{}],"x@y":[{}]}}"#;
let got = parse_installed(s);
assert!(got.contains("caveman@caveman"));
assert!(got.contains("x@y"));
}
#[test]
fn empty_on_missing_keys() {
assert!(parse_enabled("{}").is_empty());
assert!(parse_installed("{}").is_empty());
assert!(parse_enabled("not json").is_empty());
assert!(parse_installed("not json").is_empty());
}
}