redesign skills library (#8868)
Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
This commit is contained in:
@@ -30,9 +30,9 @@ pub fn global_skills_dir() -> Option<PathBuf> {
|
||||
}
|
||||
|
||||
/// Canonical writable location for project-scoped skills:
|
||||
/// `<project>/.goose/skills`.
|
||||
/// `<project>/.agents/skills`.
|
||||
pub fn project_skills_dir(project_dir: &Path) -> PathBuf {
|
||||
project_dir.join(".goose").join("skills")
|
||||
project_dir.join(".agents").join("skills")
|
||||
}
|
||||
|
||||
pub(crate) fn skills_dir_global_or_err() -> Result<PathBuf, Error> {
|
||||
@@ -196,9 +196,9 @@ pub fn all_skill_dirs(working_dir: Option<&Path>) -> Vec<(PathBuf, bool)> {
|
||||
let mut dirs: Vec<(PathBuf, bool)> = Vec::new();
|
||||
|
||||
if let Some(wd) = working_dir {
|
||||
dirs.push((wd.join(".agents").join("skills"), false));
|
||||
dirs.push((wd.join(".goose").join("skills"), false));
|
||||
dirs.push((wd.join(".claude").join("skills"), false));
|
||||
dirs.push((wd.join(".agents").join("skills"), false));
|
||||
}
|
||||
|
||||
let home = dirs::home_dir();
|
||||
|
||||
@@ -377,7 +377,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let portable_dir = project_a.join(".goose").join("skills").join("portable");
|
||||
let portable_dir = project_a.join(".agents").join("skills").join("portable");
|
||||
let (json, filename) =
|
||||
export_source(SourceType::Skill, portable_dir.to_str().unwrap()).unwrap();
|
||||
assert_eq!(filename, "portable.skill.json");
|
||||
@@ -538,7 +538,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let skill_dir = tmp.path().join(".goose").join("skills").join("my-dir");
|
||||
let skill_dir = tmp.path().join(".agents").join("skills").join("my-dir");
|
||||
let updated = update_source(
|
||||
SourceType::Skill,
|
||||
skill_dir.to_str().unwrap(),
|
||||
@@ -551,6 +551,67 @@ mod tests {
|
||||
assert_eq!(updated.name, "my-dir");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_sources_reads_project_agents_skills() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let skill_dir = tmp.path().join(".agents").join("skills").join("test-skill");
|
||||
std::fs::create_dir_all(&skill_dir).unwrap();
|
||||
std::fs::write(
|
||||
skill_dir.join("SKILL.md"),
|
||||
build_skill_md("test-skill", "from agents", "Body"),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let listed =
|
||||
list_sources(Some(SourceType::Skill), Some(tmp.path().to_str().unwrap())).unwrap();
|
||||
let skill = listed
|
||||
.iter()
|
||||
.find(|source| source.name == "test-skill" && !source.global)
|
||||
.unwrap();
|
||||
assert!(skill.directory.contains(".agents/skills"));
|
||||
assert_eq!(skill.description, "from agents");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn project_sources_prefer_agents_directory_over_legacy_goose() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let agents_skill_dir = tmp
|
||||
.path()
|
||||
.join(".agents")
|
||||
.join("skills")
|
||||
.join("shared-skill");
|
||||
let legacy_skill_dir = tmp
|
||||
.path()
|
||||
.join(".goose")
|
||||
.join("skills")
|
||||
.join("shared-skill");
|
||||
std::fs::create_dir_all(&agents_skill_dir).unwrap();
|
||||
std::fs::create_dir_all(&legacy_skill_dir).unwrap();
|
||||
std::fs::write(
|
||||
agents_skill_dir.join("SKILL.md"),
|
||||
build_skill_md("shared-skill", "preferred", "Agents"),
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(
|
||||
legacy_skill_dir.join("SKILL.md"),
|
||||
build_skill_md("shared-skill", "legacy", "Goose"),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let listed =
|
||||
list_sources(Some(SourceType::Skill), Some(tmp.path().to_str().unwrap())).unwrap();
|
||||
let matching: Vec<_> = listed
|
||||
.iter()
|
||||
.filter(|source| source.name == "shared-skill" && !source.global)
|
||||
.collect();
|
||||
assert_eq!(matching.len(), 1);
|
||||
assert!(matching[0].directory.contains(".agents/skills"));
|
||||
assert_eq!(matching[0].description, "preferred");
|
||||
|
||||
let exported = export_source(SourceType::Skill, matching[0].directory.as_str()).unwrap();
|
||||
assert!(exported.0.contains("preferred"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_rejects_path_traversal() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user