From 7a30848f659eae3f80e348ce9ccbf8eec6dbcef6 Mon Sep 17 00:00:00 2001 From: xiaoski Date: Fri, 15 May 2026 10:39:35 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9skill=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/skills/mod.rs | 64 +++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/src/skills/mod.rs b/src/skills/mod.rs index 3a23748..d3372ad 100644 --- a/src/skills/mod.rs +++ b/src/skills/mod.rs @@ -55,7 +55,7 @@ impl SkillsLoader { let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); Self { picobot_skills_dir: home.join(".picobot/skills"), - agent_skills_dir: home.join(".agent/skills"), + agent_skills_dir: home.join(".agents/skills"), workspace_skills_dir: None, state: Arc::new(Mutex::new(SkillsState::default())), } @@ -76,7 +76,9 @@ impl SkillsLoader { self.workspace_skills_dir = Some(workspace_path.join("skills")); } - /// Load all skills from both directories and record modification times + /// Load all skills from all directories and record modification times. + /// Priority: workspace > ~/.picobot/skills > ~/.agents/skills. + /// Same-name skills from higher-priority directories replace lower-priority ones. pub fn load_skills(&self) { let mut state = self.state.lock().unwrap(); state.loaded_skills.clear(); @@ -90,19 +92,7 @@ impl SkillsLoader { } } - // Load from ~/.picobot/skills - if self.picobot_skills_dir.exists() { - let loaded = self.load_skills_from_dir(&self.picobot_skills_dir); - tracing::debug!( - dir = %self.picobot_skills_dir.display(), - count = loaded.len(), - "Loaded skills from picobot directory" - ); - state.loaded_skills.extend(loaded); - state.last_picobot_mtime = Self::get_dir_mtime(&self.picobot_skills_dir); - } - - // Load from ~/.agent/skills + // Load from ~/.agents/skills (lowest priority) if self.agent_skills_dir.exists() { let loaded = self.load_skills_from_dir(&self.agent_skills_dir); tracing::debug!( @@ -114,16 +104,48 @@ impl SkillsLoader { state.last_agent_mtime = Self::get_dir_mtime(&self.agent_skills_dir); } - // Load from workspace ./skills (if set) + // Load from ~/.picobot/skills (medium priority) — replace same-name skills + if self.picobot_skills_dir.exists() { + let loaded = self.load_skills_from_dir(&self.picobot_skills_dir); + let count = loaded.len(); + let mut replaced = 0usize; + for skill in loaded { + if let Some(existing) = state.loaded_skills.iter_mut().find(|s| s.name == skill.name) { + *existing = skill; + replaced += 1; + } else { + state.loaded_skills.push(skill); + } + } + tracing::debug!( + dir = %self.picobot_skills_dir.display(), + count = count, + replaced = replaced, + "Loaded skills from picobot directory" + ); + state.last_picobot_mtime = Self::get_dir_mtime(&self.picobot_skills_dir); + } + + // Load from workspace skills dir (highest priority) — replace same-name skills if let Some(ref ws_dir) = self.workspace_skills_dir && ws_dir.exists() { let loaded = self.load_skills_from_dir(ws_dir); + let count = loaded.len(); + let mut replaced = 0usize; + for skill in loaded { + if let Some(existing) = state.loaded_skills.iter_mut().find(|s| s.name == skill.name) { + *existing = skill; + replaced += 1; + } else { + state.loaded_skills.push(skill); + } + } tracing::debug!( dir = %ws_dir.display(), - count = loaded.len(), + count = count, + replaced = replaced, "Loaded skills from workspace directory" ); - state.loaded_skills.extend(loaded); state.last_workspace_mtime = Self::get_dir_mtime(ws_dir); } @@ -245,10 +267,10 @@ impl SkillsLoader { // Directory conventions prompt.push_str("### 目录说明\n\n"); - prompt.push_str("- `~/.agent/skills/` — 外部共享 skill 目录(第三方、系统级 skill)\n"); + prompt.push_str("- `~/.agents/skills/` — 外部共享 skill 目录(第三方、系统级 skill)\n"); prompt.push_str("- `~/.picobot/skills/` — 安装 skill 的默认目录\n"); - prompt.push_str("- `./skills/` — 工作目录下的 skill,picobot 自行创建的 skill 存放于此\n\n"); - prompt.push_str("安装或创建 skill 时请按上述目录规范存放。\n\n"); + prompt.push_str("- `{workspace}/skills/` — 工作目录下的 skill,picobot 自行创建的 skill 存放于此\n\n"); + prompt.push_str("安装或创建 skill 时请按上述目录规范存放,创建skill时不要和已有skill同名。\n\n"); // Always skills summary let always_skills: Vec<_> = state.loaded_skills.iter().filter(|s| s.always).collect();