Compare commits
No commits in common. "e0a7f67dab00f9241d1cd09e747a896fe27a7307" and "4f7a8ed6454dbd8ed7574f046fed9c1028515383" have entirely different histories.
e0a7f67dab
...
4f7a8ed645
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,4 +9,3 @@ Cargo.lock
|
|||||||
.venv
|
.venv
|
||||||
PicoBot.code-workspace
|
PicoBot.code-workspace
|
||||||
.picobot
|
.picobot
|
||||||
.claude
|
|
||||||
|
|||||||
@ -72,10 +72,8 @@ fn default_skills_sources() -> Vec<String> {
|
|||||||
vec![
|
vec![
|
||||||
"user".to_string(),
|
"user".to_string(),
|
||||||
"user_agent".to_string(),
|
"user_agent".to_string(),
|
||||||
"user_openclaw".to_string(),
|
|
||||||
"project".to_string(),
|
"project".to_string(),
|
||||||
"project_agent".to_string(),
|
"project_agent".to_string(),
|
||||||
"project_openclaw".to_string(),
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -894,10 +892,8 @@ mod tests {
|
|||||||
vec![
|
vec![
|
||||||
"user".to_string(),
|
"user".to_string(),
|
||||||
"user_agent".to_string(),
|
"user_agent".to_string(),
|
||||||
"user_openclaw".to_string(),
|
|
||||||
"project".to_string(),
|
"project".to_string(),
|
||||||
"project_agent".to_string(),
|
"project_agent".to_string(),
|
||||||
"project_openclaw".to_string(),
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,10 +28,8 @@ pub struct Skill {
|
|||||||
pub enum SkillSource {
|
pub enum SkillSource {
|
||||||
User,
|
User,
|
||||||
UserAgent,
|
UserAgent,
|
||||||
UserOpenclaw,
|
|
||||||
Project,
|
Project,
|
||||||
ProjectAgent,
|
ProjectAgent,
|
||||||
ProjectOpenclaw,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
@ -329,10 +327,8 @@ impl SkillSource {
|
|||||||
match self {
|
match self {
|
||||||
SkillSource::User => "user",
|
SkillSource::User => "user",
|
||||||
SkillSource::UserAgent => "user_agent",
|
SkillSource::UserAgent => "user_agent",
|
||||||
SkillSource::UserOpenclaw => "user_openclaw",
|
|
||||||
SkillSource::Project => "project",
|
SkillSource::Project => "project",
|
||||||
SkillSource::ProjectAgent => "project_agent",
|
SkillSource::ProjectAgent => "project_agent",
|
||||||
SkillSource::ProjectOpenclaw => "project_openclaw",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -559,11 +555,6 @@ fn source_order(sources: &[String]) -> Vec<SkillSource> {
|
|||||||
result.push(SkillSource::UserAgent);
|
result.push(SkillSource::UserAgent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"user_openclaw" => {
|
|
||||||
if !result.contains(&SkillSource::UserOpenclaw) {
|
|
||||||
result.push(SkillSource::UserOpenclaw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"project" => {
|
"project" => {
|
||||||
if !result.contains(&SkillSource::Project) {
|
if !result.contains(&SkillSource::Project) {
|
||||||
result.push(SkillSource::Project);
|
result.push(SkillSource::Project);
|
||||||
@ -574,11 +565,6 @@ fn source_order(sources: &[String]) -> Vec<SkillSource> {
|
|||||||
result.push(SkillSource::ProjectAgent);
|
result.push(SkillSource::ProjectAgent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"project_openclaw" => {
|
|
||||||
if !result.contains(&SkillSource::ProjectOpenclaw) {
|
|
||||||
result.push(SkillSource::ProjectOpenclaw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unknown => {
|
unknown => {
|
||||||
tracing::warn!(source = %unknown, "Unknown skills source ignored");
|
tracing::warn!(source = %unknown, "Unknown skills source ignored");
|
||||||
}
|
}
|
||||||
@ -589,10 +575,8 @@ fn source_order(sources: &[String]) -> Vec<SkillSource> {
|
|||||||
vec![
|
vec![
|
||||||
SkillSource::User,
|
SkillSource::User,
|
||||||
SkillSource::UserAgent,
|
SkillSource::UserAgent,
|
||||||
SkillSource::UserOpenclaw,
|
|
||||||
SkillSource::Project,
|
SkillSource::Project,
|
||||||
SkillSource::ProjectAgent,
|
SkillSource::ProjectAgent,
|
||||||
SkillSource::ProjectOpenclaw,
|
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
result
|
result
|
||||||
@ -619,45 +603,28 @@ fn project_agent_skills_root(cwd: &Path) -> PathBuf {
|
|||||||
cwd.join(".agents").join("skills")
|
cwd.join(".agents").join("skills")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn project_openclaw_skills_root(cwd: &Path) -> PathBuf {
|
|
||||||
cwd.join(".openclaw").join("skills")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn project_skill_state_path(cwd: &Path) -> PathBuf {
|
fn project_skill_state_path(cwd: &Path) -> PathBuf {
|
||||||
cwd.join(".picobot").join("skill-state.json")
|
cwd.join(".picobot").join("skill-state.json")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn home_dir() -> Option<PathBuf> {
|
|
||||||
// First check HOME environment variable (useful for testing)
|
|
||||||
std::env::var_os("HOME")
|
|
||||||
.map(PathBuf::from)
|
|
||||||
.or_else(|| dirs::home_dir())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn user_skills_root() -> Option<PathBuf> {
|
fn user_skills_root() -> Option<PathBuf> {
|
||||||
home_dir().map(|p| p.join(".picobot").join("skills"))
|
dirs::home_dir().map(|p| p.join(".picobot").join("skills"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn user_skill_state_path() -> Option<PathBuf> {
|
fn user_skill_state_path() -> Option<PathBuf> {
|
||||||
home_dir().map(|p| p.join(".picobot").join("skill-state.json"))
|
dirs::home_dir().map(|p| p.join(".picobot").join("skill-state.json"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn user_agent_skills_root() -> Option<PathBuf> {
|
fn user_agent_skills_root() -> Option<PathBuf> {
|
||||||
home_dir().map(|p| p.join(".agents").join("skills"))
|
dirs::home_dir().map(|p| p.join(".agents").join("skills"))
|
||||||
}
|
|
||||||
|
|
||||||
fn user_openclaw_skills_root() -> Option<PathBuf> {
|
|
||||||
home_dir().map(|p| p.join(".openclaw").join("skills"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source_root(source: SkillSource, cwd: &Path) -> Option<PathBuf> {
|
fn source_root(source: SkillSource, cwd: &Path) -> Option<PathBuf> {
|
||||||
match source {
|
match source {
|
||||||
SkillSource::User => user_skills_root(),
|
SkillSource::User => user_skills_root(),
|
||||||
SkillSource::UserAgent => user_agent_skills_root(),
|
SkillSource::UserAgent => user_agent_skills_root(),
|
||||||
SkillSource::UserOpenclaw => user_openclaw_skills_root(),
|
|
||||||
SkillSource::Project => Some(cwd.join(".picobot").join("skills")),
|
SkillSource::Project => Some(cwd.join(".picobot").join("skills")),
|
||||||
SkillSource::ProjectAgent => Some(project_agent_skills_root(cwd)),
|
SkillSource::ProjectAgent => Some(project_agent_skills_root(cwd)),
|
||||||
SkillSource::ProjectOpenclaw => Some(project_openclaw_skills_root(cwd)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1056,10 +1023,8 @@ mod tests {
|
|||||||
let ordered = source_order(&[
|
let ordered = source_order(&[
|
||||||
"user".to_string(),
|
"user".to_string(),
|
||||||
"user_agent".to_string(),
|
"user_agent".to_string(),
|
||||||
"user_openclaw".to_string(),
|
|
||||||
"project".to_string(),
|
"project".to_string(),
|
||||||
"project_agent".to_string(),
|
"project_agent".to_string(),
|
||||||
"project_openclaw".to_string(),
|
|
||||||
"project".to_string(),
|
"project".to_string(),
|
||||||
"unknown".to_string(),
|
"unknown".to_string(),
|
||||||
]);
|
]);
|
||||||
@ -1069,10 +1034,8 @@ mod tests {
|
|||||||
vec![
|
vec![
|
||||||
SkillSource::User,
|
SkillSource::User,
|
||||||
SkillSource::UserAgent,
|
SkillSource::UserAgent,
|
||||||
SkillSource::UserOpenclaw,
|
|
||||||
SkillSource::Project,
|
SkillSource::Project,
|
||||||
SkillSource::ProjectAgent,
|
SkillSource::ProjectAgent,
|
||||||
SkillSource::ProjectOpenclaw,
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1281,72 +1244,4 @@ mod tests {
|
|||||||
assert!(user_enabled.disabled_in_scopes.is_empty());
|
assert!(user_enabled.disabled_in_scopes.is_empty());
|
||||||
assert!(runtime.get_skill("demo").is_some());
|
assert!(runtime.get_skill("demo").is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_discover_loads_project_openclaw_skills() {
|
|
||||||
let _lock = acquire_test_lock();
|
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
|
||||||
let home_dir = temp_dir.path().join("home");
|
|
||||||
let project_dir = temp_dir.path().join("project");
|
|
||||||
fs::create_dir_all(&home_dir).unwrap();
|
|
||||||
fs::create_dir_all(&project_dir).unwrap();
|
|
||||||
let _home = HomeDirGuard::enter(&home_dir);
|
|
||||||
let _guard = CurrentDirGuard::enter(&project_dir);
|
|
||||||
|
|
||||||
let openclaw_skill_dir = project_dir
|
|
||||||
.join(".openclaw")
|
|
||||||
.join("skills")
|
|
||||||
.join("demo-openclaw");
|
|
||||||
fs::create_dir_all(&openclaw_skill_dir).unwrap();
|
|
||||||
fs::write(
|
|
||||||
openclaw_skill_dir.join("SKILL.md"),
|
|
||||||
"---\ndescription: openclaw skill\n---\nUse openclaw",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let catalog = SkillCatalog::discover(&SkillsConfig {
|
|
||||||
enabled: true,
|
|
||||||
sources: vec!["project_openclaw".to_string()],
|
|
||||||
max_index_chars: 4000,
|
|
||||||
max_listed_skills: 32,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(catalog.len(), 1);
|
|
||||||
let payload = catalog.activation_event_payload("demo-openclaw").unwrap();
|
|
||||||
assert_eq!(payload["source"], "project_openclaw");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_discover_loads_user_openclaw_skills() {
|
|
||||||
let _lock = acquire_test_lock();
|
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
|
||||||
let home_dir = temp_dir.path().join("home");
|
|
||||||
let project_dir = temp_dir.path().join("project");
|
|
||||||
fs::create_dir_all(&home_dir).unwrap();
|
|
||||||
fs::create_dir_all(&project_dir).unwrap();
|
|
||||||
let _home = HomeDirGuard::enter(&home_dir);
|
|
||||||
let _guard = CurrentDirGuard::enter(&project_dir);
|
|
||||||
|
|
||||||
let user_openclaw_skill_dir = home_dir
|
|
||||||
.join(".openclaw")
|
|
||||||
.join("skills")
|
|
||||||
.join("demo-user-openclaw");
|
|
||||||
fs::create_dir_all(&user_openclaw_skill_dir).unwrap();
|
|
||||||
fs::write(
|
|
||||||
user_openclaw_skill_dir.join("SKILL.md"),
|
|
||||||
"---\ndescription: user openclaw skill\n---\nUse user openclaw",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let catalog = SkillCatalog::discover(&SkillsConfig {
|
|
||||||
enabled: true,
|
|
||||||
sources: vec!["user_openclaw".to_string()],
|
|
||||||
max_index_chars: 4000,
|
|
||||||
max_listed_skills: 32,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(catalog.len(), 1);
|
|
||||||
let payload = catalog.activation_event_payload("demo-user-openclaw").unwrap();
|
|
||||||
assert_eq!(payload["source"], "user_openclaw");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,7 +32,7 @@ impl Tool for SkillManageTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
"Manage PicoBot skills stored under .picobot/skills or ~/.picobot/skills, while discovery also reads .agents/skills, ~/.agents/skills, .openclaw/skills, and ~/.openclaw/skills. Supports actions: list, get, create, update, delete, disable, reload."
|
"Manage PicoBot skills stored under .picobot/skills or ~/.picobot/skills, while discovery also reads .agents/skills and ~/.agents/skills. Supports actions: list, get, create, update, delete, disable, reload."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parameters_schema(&self) -> serde_json::Value {
|
fn parameters_schema(&self) -> serde_json::Value {
|
||||||
@ -121,10 +121,8 @@ impl Tool for SkillManageTool {
|
|||||||
"source": match skill.source {
|
"source": match skill.source {
|
||||||
crate::skills::SkillSource::User => "user",
|
crate::skills::SkillSource::User => "user",
|
||||||
crate::skills::SkillSource::UserAgent => "user_agent",
|
crate::skills::SkillSource::UserAgent => "user_agent",
|
||||||
crate::skills::SkillSource::UserOpenclaw => "user_openclaw",
|
|
||||||
crate::skills::SkillSource::Project => "project",
|
crate::skills::SkillSource::Project => "project",
|
||||||
crate::skills::SkillSource::ProjectAgent => "project_agent",
|
crate::skills::SkillSource::ProjectAgent => "project_agent",
|
||||||
crate::skills::SkillSource::ProjectOpenclaw => "project_openclaw",
|
|
||||||
},
|
},
|
||||||
"path": skill.path.display().to_string(),
|
"path": skill.path.display().to_string(),
|
||||||
}),
|
}),
|
||||||
@ -325,10 +323,8 @@ fn list_skills_payload(skills: &Arc<SkillRuntime>) -> serde_json::Value {
|
|||||||
"source": match skill.source {
|
"source": match skill.source {
|
||||||
crate::skills::SkillSource::User => "user",
|
crate::skills::SkillSource::User => "user",
|
||||||
crate::skills::SkillSource::UserAgent => "user_agent",
|
crate::skills::SkillSource::UserAgent => "user_agent",
|
||||||
crate::skills::SkillSource::UserOpenclaw => "user_openclaw",
|
|
||||||
crate::skills::SkillSource::Project => "project",
|
crate::skills::SkillSource::Project => "project",
|
||||||
crate::skills::SkillSource::ProjectAgent => "project_agent",
|
crate::skills::SkillSource::ProjectAgent => "project_agent",
|
||||||
crate::skills::SkillSource::ProjectOpenclaw => "project_openclaw",
|
|
||||||
},
|
},
|
||||||
"path": skill.path.display().to_string(),
|
"path": skill.path.display().to_string(),
|
||||||
})).collect::<Vec<_>>()
|
})).collect::<Vec<_>>()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user