use crate::agent::runloop::ResumeSession;
use crate::agent::runloop::unified::session_setup::init::refresh_tool_snapshot;
use crate::agent::runloop::unified::tool_catalog::{
ToolCatalogState, tool_catalog_change_notifier,
};
use anyhow::{Context, Result};
use hashbrown::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{info, warn};
use vtcode_config::constants::tools as tool_constants;
use vtcode_core::config::loader::VTCodeConfig;
use vtcode_core::config::types::AgentConfig as CoreAgentConfig;
use vtcode_core::llm::provider as uni;
use vtcode_core::tools::ToolRegistry;
use vtcode_core::tools::handlers::{DeferredToolPolicy, ToolModelCapabilities};
use vtcode_core::tools::native_cgp_tool_factory;
use vtcode_core::tools::registry::ToolCatalogSource;
pub(crate) struct SkillSetupState {
pub active_skills_map: Arc<RwLock<HashMap<String, vtcode_core::skills::types::Skill>>>,
}
pub(crate) async fn discover_skills(
config: &CoreAgentConfig,
resume: Option<&ResumeSession>,
) -> SkillSetupState {
let active_skills_map = Arc::new(RwLock::new(HashMap::new()));
info!(
workspace = %config.workspace.display(),
"Deferring skill discovery until an explicit /skills command"
);
if let Some(resume_session) = resume
&& !resume_session.loaded_skills().is_empty()
{
warn!(
"Skipping loaded skill restore during startup; use /skills load to reactivate session skills"
);
}
SkillSetupState { active_skills_map }
}
pub(crate) async fn register_skill_tools(
tool_registry: &mut ToolRegistry,
tools: &Arc<RwLock<Vec<uni::ToolDefinition>>>,
tool_catalog: &Arc<ToolCatalogState>,
config: &CoreAgentConfig,
vt_cfg: Option<&VTCodeConfig>,
tool_documentation_mode: vtcode_core::config::ToolDocumentationMode,
deferred_tool_policy: DeferredToolPolicy,
anthropic_native_memory_enabled: bool,
skill_setup: &SkillSetupState,
) -> Result<()> {
let runtime = vtcode_core::tools::skills::SkillToolSessionRuntime::new(
Arc::new(tool_registry.clone()),
Some(Arc::clone(tools)),
tool_documentation_mode,
ToolModelCapabilities::for_model_name(&config.model),
Some(tool_catalog_change_notifier(tool_catalog)),
)
.with_fork_executor(Arc::new(
vtcode_core::skills::executor::ChildAgentSkillExecutor::new(
Arc::new(tool_registry.clone()),
vtcode_core::skills::executor::ForkSkillRuntimeConfig {
workspace: config.workspace.clone(),
model: config.model.clone(),
api_key: config.api_key.clone(),
openai_chatgpt_auth: config.openai_chatgpt_auth.clone(),
vt_cfg: vt_cfg.cloned(),
},
),
))
.with_deferred_tool_policy(deferred_tool_policy.clone());
let runtime = runtime.with_anthropic_native_memory_enabled(anthropic_native_memory_enabled);
register_list_skills_tool(
tool_registry,
config.workspace.clone(),
Arc::clone(&skill_setup.active_skills_map),
)
.await?;
register_load_skill_resource_tool(tool_registry, Arc::clone(&skill_setup.active_skills_map))
.await?;
register_load_skill_tool(
tool_registry,
config.workspace.clone(),
Arc::clone(&skill_setup.active_skills_map),
runtime,
)
.await?;
refresh_tool_snapshot(
tool_registry,
tools,
tool_catalog,
config,
vt_cfg,
tool_documentation_mode,
&deferred_tool_policy,
)
.await;
Ok(())
}
async fn register_list_skills_tool(
tool_registry: &mut ToolRegistry,
workspace_root: std::path::PathBuf,
active_skills_map: Arc<RwLock<HashMap<String, vtcode_core::skills::types::Skill>>>,
) -> Result<()> {
let list_skills_tool = vtcode_core::tools::skills::ListSkillsTool::new(
workspace_root.clone(),
Arc::clone(&active_skills_map),
);
let list_skills_reg = vtcode_core::tools::registry::ToolRegistration::from_tool_instance(
tool_constants::LIST_SKILLS,
vtcode_core::config::types::CapabilityLevel::Basic,
list_skills_tool,
)
.with_catalog_source(ToolCatalogSource::Builtin)
.with_native_cgp_factory(native_cgp_tool_factory({
let workspace_root = workspace_root.clone();
let active_skills_map = Arc::clone(&active_skills_map);
move || {
vtcode_core::tools::skills::ListSkillsTool::new(
workspace_root.clone(),
Arc::clone(&active_skills_map),
)
}
}));
tool_registry
.register_tool(list_skills_reg)
.await
.context("Failed to register list_skills tool")?;
Ok(())
}
async fn register_load_skill_resource_tool(
tool_registry: &mut ToolRegistry,
active_skills_map: Arc<RwLock<HashMap<String, vtcode_core::skills::types::Skill>>>,
) -> Result<()> {
let load_resource_tool =
vtcode_core::tools::skills::LoadSkillResourceTool::new(Arc::clone(&active_skills_map));
let load_resource_reg = vtcode_core::tools::registry::ToolRegistration::from_tool_instance(
tool_constants::LOAD_SKILL_RESOURCE,
vtcode_core::config::types::CapabilityLevel::Basic,
load_resource_tool,
)
.with_catalog_source(ToolCatalogSource::Builtin)
.with_native_cgp_factory(native_cgp_tool_factory({
let active_skills_map = Arc::clone(&active_skills_map);
move || {
vtcode_core::tools::skills::LoadSkillResourceTool::new(Arc::clone(&active_skills_map))
}
}));
tool_registry
.register_tool(load_resource_reg)
.await
.context("Failed to register load_skill_resource tool")?;
Ok(())
}
async fn register_load_skill_tool(
tool_registry: &mut ToolRegistry,
workspace_root: std::path::PathBuf,
active_skills_map: Arc<RwLock<HashMap<String, vtcode_core::skills::types::Skill>>>,
runtime: vtcode_core::tools::skills::SkillToolSessionRuntime,
) -> Result<()> {
let load_skill_tool = vtcode_core::tools::skills::LoadSkillTool::new(
workspace_root.clone(),
Arc::clone(&active_skills_map),
runtime.clone(),
);
let load_skill_reg = vtcode_core::tools::registry::ToolRegistration::from_tool_instance(
tool_constants::LOAD_SKILL,
vtcode_core::config::types::CapabilityLevel::Basic,
load_skill_tool,
)
.with_catalog_source(ToolCatalogSource::Builtin)
.with_native_cgp_factory(native_cgp_tool_factory({
let workspace_root = workspace_root.clone();
let active_skills_map = Arc::clone(&active_skills_map);
let runtime = runtime.clone();
move || {
vtcode_core::tools::skills::LoadSkillTool::new(
workspace_root.clone(),
Arc::clone(&active_skills_map),
runtime.clone(),
)
}
}));
tool_registry
.register_tool(load_skill_reg)
.await
.context("Failed to register load_skill tool")?;
Ok(())
}