ai_agent/utils/plugins/
plugin_directories.rs1#![allow(dead_code)]
3
4use std::path::PathBuf;
5
6use once_cell::sync::OnceCell;
7
8const PLUGINS_DIR: &str = "plugins";
9const COWORK_PLUGINS_DIR: &str = "cowork_plugins";
10
11static PLUGINS_DIR_NAME: OnceCell<String> = OnceCell::new();
12
13fn get_plugins_dir_name() -> &'static str {
15 PLUGINS_DIR_NAME.get_or_init(|| {
16 if std::env::var("CLAUDE_CODE_USE_COWORK_PLUGINS")
19 .map(|v| v == "1" || v.to_lowercase() == "true")
20 .unwrap_or(false)
21 {
22 return COWORK_PLUGINS_DIR.to_string();
23 }
24 PLUGINS_DIR.to_string()
25 })
26}
27
28pub fn get_plugins_directory() -> String {
30 if let Ok(env_override) = std::env::var("CLAUDE_CODE_PLUGIN_CACHE_DIR") {
32 return expand_tilde(&env_override);
33 }
34
35 let config_home = crate::utils::env_utils::get_claude_config_home_dir();
36 format!("{}/{}", config_home, get_plugins_dir_name())
37}
38
39pub fn get_plugin_seed_dirs() -> Vec<PathBuf> {
41 let raw = match std::env::var("CLAUDE_CODE_PLUGIN_SEED_DIR") {
42 Ok(v) => v,
43 Err(_) => return Vec::new(),
44 };
45
46 let delimiter = if cfg!(windows) { ";" } else { ":" };
47
48 raw.split(delimiter)
49 .filter(|s| !s.is_empty())
50 .map(|s| PathBuf::from(expand_tilde(s)))
51 .collect()
52}
53
54fn sanitize_plugin_id(plugin_id: &str) -> String {
56 plugin_id
57 .chars()
58 .map(|c| {
59 if c.is_alphanumeric() || c == '-' || c == '_' {
60 c
61 } else {
62 '-'
63 }
64 })
65 .collect()
66}
67
68pub fn plugin_data_dir_path(plugin_id: &str) -> PathBuf {
70 PathBuf::from(get_plugins_directory())
71 .join("data")
72 .join(sanitize_plugin_id(plugin_id))
73}
74
75pub fn get_plugin_data_dir(plugin_id: &str) -> String {
77 let dir = plugin_data_dir_path(plugin_id);
78 std::fs::create_dir_all(&dir).ok();
79 dir.to_string_lossy().to_string()
80}
81
82pub async fn delete_plugin_data_dir(plugin_id: &str) {
84 let dir = plugin_data_dir_path(plugin_id);
85 if let Err(e) = tokio::fs::remove_dir_all(&dir).await {
86 log::debug!("Failed to delete plugin data dir {:?}: {}", dir, e);
87 }
88}
89
90fn expand_tilde(path: &str) -> String {
92 if path.starts_with("~/") || path == "~" {
93 if let Some(home) = dirs::home_dir() {
94 return format!("{}{}", home.display(), &path[1..]);
95 }
96 }
97 path.to_string()
98}