use std::path::PathBuf;
use std::sync::Arc;
use robit_ai::config::RobitConfig;
use crate::skill::{load_skills, Skill, SkillRegistry};
use crate::tool::bash::BashTool;
use crate::tool::edit::EditTool;
use crate::tool::find::FindTool;
use crate::tool::grep::GrepTool;
use crate::tool::load_skill::LoadSkillTool;
use crate::tool::ls::LsTool;
use crate::tool::read::ReadTool;
use crate::tool::write::WriteTool;
use crate::tool::ToolRegistry;
use crate::SkillLoadError;
pub struct BootstrapResult {
pub skill_registry: Arc<SkillRegistry>,
pub tool_registry: Arc<ToolRegistry>,
pub total_skills_loaded: usize,
pub skill_load_errors: Vec<SkillLoadError>,
}
pub fn bootstrap(
config: &RobitConfig,
working_dir: &PathBuf,
base_tool_names: &[&str],
) -> BootstrapResult {
let (skills, skill_load_errors) = load_all_skills(working_dir);
let total_skills_loaded = skills.len();
let filtered_skills = filter_skills_by_config(skills, config);
let skill_registry = Arc::new(SkillRegistry::new(filtered_skills, base_tool_names));
let tool_registry = Arc::new(create_tools_from_config(config, Arc::clone(&skill_registry)));
BootstrapResult {
skill_registry,
tool_registry,
total_skills_loaded,
skill_load_errors,
}
}
pub fn load_all_skills(working_dir: &PathBuf) -> (Vec<Skill>, Vec<SkillLoadError>) {
let global_skills_dir = dirs::home_dir().map(|h| h.join(".robit/skills"));
let project_skills_dir = Some(working_dir.join(".robit/skills"));
load_skills(global_skills_dir, project_skills_dir)
}
pub fn filter_skills_by_config(skills: Vec<Skill>, config: &RobitConfig) -> Vec<Skill> {
let enabled_skills = config.app.as_ref().and_then(|a| a.enabled_skills.as_ref());
match enabled_skills {
Some(list) => skills
.into_iter()
.filter(|s| list.contains(&s.frontmatter.name))
.collect(),
None => skills,
}
}
pub fn create_tools_from_config(
config: &RobitConfig,
skill_registry: Arc<SkillRegistry>,
) -> ToolRegistry {
let mut tools = ToolRegistry::new();
let context_config = config.app.as_ref().and_then(|a| a.context.as_ref());
let max_lines = context_config.and_then(|c| c.max_output_lines).unwrap_or(500);
let max_bytes = context_config
.and_then(|c| c.max_output_bytes)
.unwrap_or(51200);
tools.register(ReadTool::new(max_lines, max_bytes));
tools.register(LoadSkillTool::new(skill_registry));
let enabled_tools = config.app.as_ref().and_then(|a| a.enabled_tools.as_ref());
match enabled_tools {
Some(list) => {
for tool_name in list {
match tool_name.as_str() {
"read" => {} "load_skill" => {} "bash" => tools.register(BashTool::new(max_bytes)),
"write" => tools.register(WriteTool::new()),
"edit" => tools.register(EditTool::new()),
"ls" => tools.register(LsTool::new()),
"find" => tools.register(FindTool::new(max_bytes)),
"grep" => tools.register(GrepTool::new(max_lines, max_bytes)),
_ => tracing::warn!("Unknown tool in enabled_tools config: {}", tool_name),
}
}
}
None => {
tools.register(BashTool::new(max_bytes));
tools.register(WriteTool::new());
tools.register(EditTool::new());
tools.register(LsTool::new());
tools.register(FindTool::new(max_bytes));
tools.register(GrepTool::new(max_lines, max_bytes));
}
}
tools
}
pub fn log_skill_errors(errors: &[SkillLoadError]) {
for err in errors {
tracing::warn!("Skill load error: {:?}", err);
}
}