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::memory::{ForgetTool, ListMemoriesTool, MemorizeTool, RecallTool};
19use crate::tool::read::ReadTool;
20use crate::tool::search_history::SearchHistoryTool;
21use crate::tool::write::WriteTool;
22use crate::tool::ToolRegistry;
23use crate::SkillLoadError;
24
25pub struct BootstrapResult {
31 pub skill_registry: Arc<SkillRegistry>,
33 pub tool_registry: Arc<ToolRegistry>,
35 pub total_skills_loaded: usize,
37 pub skill_load_errors: Vec<SkillLoadError>,
39}
40
41pub fn bootstrap(
55 config: &RobitConfig,
56 working_dir: &PathBuf,
57 base_tool_names: &[&str],
58) -> BootstrapResult {
59 let (skills, skill_load_errors) = load_all_skills(working_dir);
60 let total_skills_loaded = skills.len();
61
62 let filtered_skills = filter_skills_by_config(skills, config);
63
64 let skill_registry = Arc::new(SkillRegistry::new(filtered_skills, base_tool_names));
65 let tool_registry = Arc::new(create_tools_from_config(config, Arc::clone(&skill_registry)));
66
67 BootstrapResult {
68 skill_registry,
69 tool_registry,
70 total_skills_loaded,
71 skill_load_errors,
72 }
73}
74
75pub fn load_all_skills(working_dir: &PathBuf) -> (Vec<Skill>, Vec<SkillLoadError>) {
79 let global_skills_dir = dirs::home_dir().map(|h| h.join(".robit/skills"));
80 let project_skills_dir = Some(working_dir.join(".robit/skills"));
81
82 load_skills(global_skills_dir, project_skills_dir)
83}
84
85pub fn filter_skills_by_config(skills: Vec<Skill>, config: &RobitConfig) -> Vec<Skill> {
87 let enabled_skills = config.app.as_ref().and_then(|a| a.enabled_skills.as_ref());
88
89 match enabled_skills {
90 Some(list) => skills
91 .into_iter()
92 .filter(|s| list.contains(&s.frontmatter.name))
93 .collect(),
94 None => skills,
95 }
96}
97
98pub fn create_tools_from_config(
104 config: &RobitConfig,
105 skill_registry: Arc<SkillRegistry>,
106) -> ToolRegistry {
107 let mut tools = ToolRegistry::new();
108 let context_config = config.app.as_ref().and_then(|a| a.context.as_ref());
109 let max_lines = context_config.and_then(|c| c.max_output_lines).unwrap_or(500);
110 let max_bytes = context_config
111 .and_then(|c| c.max_output_bytes)
112 .unwrap_or(51200);
113
114 tools.register(ReadTool::new(max_lines, max_bytes));
116 tools.register(LoadSkillTool::new(skill_registry));
117 tools.register(MemorizeTool::new());
118 tools.register(RecallTool::new());
119 tools.register(ForgetTool::new());
120 tools.register(ListMemoriesTool::new());
121 tools.register(SearchHistoryTool::new());
122
123 let enabled_tools = config.app.as_ref().and_then(|a| a.enabled_tools.as_ref());
125
126 match enabled_tools {
127 Some(list) => {
128 for tool_name in list {
130 match tool_name.as_str() {
131 "read" => {} "load_skill" => {} "memorize" => {} "recall" => {} "forget" => {} "list_memories" => {} "search_history" => {} "bash" => tools.register(BashTool::new(max_bytes)),
139 "write" => tools.register(WriteTool::new()),
140 "edit" => tools.register(EditTool::new()),
141 "ls" => tools.register(LsTool::new()),
142 "find" => tools.register(FindTool::new(max_bytes)),
143 "grep" => tools.register(GrepTool::new(max_lines, max_bytes)),
144 _ => tracing::warn!("Unknown tool in enabled_tools config: {}", tool_name),
145 }
146 }
147 }
148 None => {
149 tools.register(BashTool::new(max_bytes));
151 tools.register(WriteTool::new());
152 tools.register(EditTool::new());
153 tools.register(LsTool::new());
154 tools.register(FindTool::new(max_bytes));
155 tools.register(GrepTool::new(max_lines, max_bytes));
156 }
157 }
158
159 tools
160}
161
162pub fn log_skill_errors(errors: &[SkillLoadError]) {
166 for err in errors {
167 tracing::warn!("Skill load error: {:?}", err);
168 }
169}