1use std::path::PathBuf;
7use std::sync::Arc;
8
9use robit_ai::config::RobitConfig;
10
11use crate::skill::{load_skills, Skill, SkillRegistry};
12use crate::tool::bash::BashTool;
13use crate::tool::edit::EditTool;
14use crate::tool::find::FindTool;
15use crate::tool::grep::GrepTool;
16use crate::tool::load_skill::LoadSkillTool;
17use crate::tool::ls::LsTool;
18use crate::tool::read::ReadTool;
19use crate::tool::write::WriteTool;
20use crate::tool::ToolRegistry;
21use crate::SkillLoadError;
22
23pub struct BootstrapResult {
29 pub skill_registry: Arc<SkillRegistry>,
31 pub tool_registry: Arc<ToolRegistry>,
33 pub total_skills_loaded: usize,
35 pub skill_load_errors: Vec<SkillLoadError>,
37}
38
39pub fn bootstrap(
53 config: &RobitConfig,
54 working_dir: &PathBuf,
55 base_tool_names: &[&str],
56) -> BootstrapResult {
57 let (skills, skill_load_errors) = load_all_skills(working_dir);
58 let total_skills_loaded = skills.len();
59
60 let filtered_skills = filter_skills_by_config(skills, config);
61
62 let skill_registry = Arc::new(SkillRegistry::new(filtered_skills, base_tool_names));
63 let tool_registry = Arc::new(create_tools_from_config(config, Arc::clone(&skill_registry)));
64
65 BootstrapResult {
66 skill_registry,
67 tool_registry,
68 total_skills_loaded,
69 skill_load_errors,
70 }
71}
72
73pub fn load_all_skills(working_dir: &PathBuf) -> (Vec<Skill>, Vec<SkillLoadError>) {
77 let global_skills_dir = dirs::home_dir().map(|h| h.join(".robit/skills"));
78 let project_skills_dir = Some(working_dir.join(".robit/skills"));
79
80 load_skills(global_skills_dir, project_skills_dir)
81}
82
83pub fn filter_skills_by_config(skills: Vec<Skill>, config: &RobitConfig) -> Vec<Skill> {
85 let enabled_skills = config.app.as_ref().and_then(|a| a.enabled_skills.as_ref());
86
87 match enabled_skills {
88 Some(list) => skills
89 .into_iter()
90 .filter(|s| list.contains(&s.frontmatter.name))
91 .collect(),
92 None => skills,
93 }
94}
95
96pub fn create_tools_from_config(
102 config: &RobitConfig,
103 skill_registry: Arc<SkillRegistry>,
104) -> ToolRegistry {
105 let mut tools = ToolRegistry::new();
106 let context_config = config.app.as_ref().and_then(|a| a.context.as_ref());
107 let max_lines = context_config.and_then(|c| c.max_output_lines).unwrap_or(500);
108 let max_bytes = context_config
109 .and_then(|c| c.max_output_bytes)
110 .unwrap_or(51200);
111
112 tools.register(ReadTool::new(max_lines, max_bytes));
114 tools.register(LoadSkillTool::new(skill_registry));
115
116 let enabled_tools = config.app.as_ref().and_then(|a| a.enabled_tools.as_ref());
118
119 match enabled_tools {
120 Some(list) => {
121 for tool_name in list {
123 match tool_name.as_str() {
124 "read" => {} "load_skill" => {} "bash" => tools.register(BashTool::new(max_bytes)),
127 "write" => tools.register(WriteTool::new()),
128 "edit" => tools.register(EditTool::new()),
129 "ls" => tools.register(LsTool::new()),
130 "find" => tools.register(FindTool::new(max_bytes)),
131 "grep" => tools.register(GrepTool::new(max_lines, max_bytes)),
132 _ => tracing::warn!("Unknown tool in enabled_tools config: {}", tool_name),
133 }
134 }
135 }
136 None => {
137 tools.register(BashTool::new(max_bytes));
139 tools.register(WriteTool::new());
140 tools.register(EditTool::new());
141 tools.register(LsTool::new());
142 tools.register(FindTool::new(max_bytes));
143 tools.register(GrepTool::new(max_lines, max_bytes));
144 }
145 }
146
147 tools
148}
149
150pub fn log_skill_errors(errors: &[SkillLoadError]) {
154 for err in errors {
155 tracing::warn!("Skill load error: {:?}", err);
156 }
157}