vtcode_core/subagents/
discovery.rs1use anyhow::{Context, Result};
2use std::path::{Path, PathBuf};
3use vtcode_config::{DiscoveredSubagents, SubagentDiscoveryInput, discover_subagents};
4
5use crate::plugins::components::AgentsHandler;
6use crate::plugins::manifest::PluginManifest;
7use vtcode_commons::VtCodePaths;
8
9pub async fn discover_controller_subagents(workspace_root: &Path) -> Result<DiscoveredSubagents> {
14 let plugin_agent_files = discover_plugin_agent_files(workspace_root).await?;
15 let mut input = SubagentDiscoveryInput::new(workspace_root.to_path_buf());
16 input.plugin_agent_files = plugin_agent_files;
17 discover_subagents(&input)
18}
19
20async fn discover_plugin_agent_files(workspace_root: &Path) -> Result<Vec<(String, PathBuf)>> {
21 let mut files = Vec::new();
22 for plugin_root in trusted_plugin_roots(workspace_root) {
23 if !plugin_root.exists() || !plugin_root.is_dir() {
24 continue;
25 }
26
27 let mut plugin_entries = tokio::fs::read_dir(&plugin_root)
28 .await
29 .with_context(|| format!("Failed to read plugin directory {}", plugin_root.display()))?;
30 while let Some(entry) = plugin_entries.next_entry().await? {
31 let path = entry.path();
32 if !path.is_dir() {
33 continue;
34 }
35 let manifest_path = path.join(".vtcode-plugin/plugin.json");
36 if !manifest_path.exists() {
37 continue;
38 }
39
40 let manifest: PluginManifest = serde_json::from_str(
41 &tokio::fs::read_to_string(&manifest_path)
42 .await
43 .with_context(|| format!("Failed to read plugin manifest {}", manifest_path.display()))?,
44 )
45 .with_context(|| format!("Failed to parse plugin manifest {}", manifest_path.display()))?;
46 for agent_path in AgentsHandler::process_agents(&path, manifest.agents.clone()).await? {
47 files.push((manifest.name.clone(), agent_path));
48 }
49 }
50 }
51 Ok(files)
52}
53
54fn trusted_plugin_roots(workspace_root: &Path) -> Vec<PathBuf> {
55 let mut roots = Vec::with_capacity(4);
56 if let Some(codex_home) = std::env::var_os("CODEX_HOME").map(PathBuf::from) {
57 roots.push(codex_home.join("plugins"));
58 } else if let Ok(paths) = VtCodePaths::resolve() {
59 roots.push(paths.plugins_dir());
60 roots.push(paths.legacy_dir().join("plugins"));
61 }
62 roots.push(workspace_root.join(".vtcode/plugins"));
63 roots.push(workspace_root.join(".agents/plugins"));
64 roots
65}