Nothing
# Tests for SkillRegistry class
library(testthat)
library(aisdk)
# Get path to test fixtures
fixtures_path <- testthat::test_path("fixtures", "skills")
# === Tests for SkillRegistry initialization ===
test_that("SkillRegistry can be created empty", {
registry <- SkillRegistry$new()
expect_s3_class(registry, "SkillRegistry")
expect_equal(registry$count(), 0)
})
test_that("SkillRegistry can be created with path", {
skip_if_not(dir.exists(fixtures_path), "Test fixtures not found")
registry <- SkillRegistry$new(fixtures_path)
expect_s3_class(registry, "SkillRegistry")
expect_gte(registry$count(), 1)
})
# === Tests for SkillRegistry$scan_skills ===
test_that("SkillRegistry$scan_skills finds skills", {
skip_if_not(dir.exists(fixtures_path), "Test fixtures not found")
registry <- SkillRegistry$new()
registry$scan_skills(fixtures_path)
expect_gte(registry$count(), 1)
expect_true(registry$has_skill("test_skill"))
})
test_that("SkillRegistry$scan_skills aborts for missing directory", {
registry <- SkillRegistry$new()
expect_error(registry$scan_skills("/nonexistent/path"), "does not exist")
})
# === Tests for SkillRegistry$get_skill ===
test_that("SkillRegistry$get_skill returns skill by name", {
skip_if_not(dir.exists(fixtures_path), "Test fixtures not found")
registry <- SkillRegistry$new(fixtures_path)
skill <- registry$get_skill("test_skill")
expect_s3_class(skill, "Skill")
expect_equal(skill$name, "test_skill")
})
test_that("SkillRegistry$get_skill returns NULL for missing skill", {
registry <- SkillRegistry$new()
expect_null(registry$get_skill("nonexistent"))
})
# === Tests for SkillRegistry$has_skill ===
test_that("SkillRegistry$has_skill returns correct boolean", {
skip_if_not(dir.exists(fixtures_path), "Test fixtures not found")
registry <- SkillRegistry$new(fixtures_path)
expect_true(registry$has_skill("test_skill"))
expect_false(registry$has_skill("nonexistent"))
})
# === Tests for SkillRegistry$list_skills ===
test_that("SkillRegistry$list_skills returns data.frame", {
skip_if_not(dir.exists(fixtures_path), "Test fixtures not found")
registry <- SkillRegistry$new(fixtures_path)
skills <- registry$list_skills()
expect_s3_class(skills, "data.frame")
expect_true("name" %in% names(skills))
expect_true("description" %in% names(skills))
expect_true("aliases" %in% names(skills))
expect_true("when_to_use" %in% names(skills))
expect_true("paths" %in% names(skills))
expect_gte(nrow(skills), 1)
})
test_that("SkillRegistry$list_skills returns empty data.frame when empty", {
registry <- SkillRegistry$new()
skills <- registry$list_skills()
expect_s3_class(skills, "data.frame")
expect_equal(nrow(skills), 0)
})
# === Tests for SkillRegistry$generate_prompt_section ===
test_that("SkillRegistry$generate_prompt_section returns formatted string", {
skip_if_not(dir.exists(fixtures_path), "Test fixtures not found")
registry <- SkillRegistry$new(fixtures_path)
prompt <- registry$generate_prompt_section()
expect_type(prompt, "character")
expect_true(grepl("Available Skills", prompt))
expect_true(grepl("test_skill", prompt))
expect_true(grepl("load_skill", prompt))
expect_true(grepl("even if the user does not know the skill name", prompt, fixed = TRUE))
})
test_that("SkillRegistry$generate_prompt_section returns empty string when empty", {
registry <- SkillRegistry$new()
prompt <- registry$generate_prompt_section()
expect_equal(prompt, "")
})
test_that("SkillRegistry resolves aliases and fuzzy names", {
temp_dir <- tempfile()
dir.create(temp_dir)
on.exit(unlink(temp_dir, recursive = TRUE))
dir.create(file.path(temp_dir, "mentor-skill"))
writeLines(c(
"---",
"name: mentor-skill",
"description: mentor skill",
"aliases:",
" - yshu",
" - Y叔",
"---",
"Body"
), file.path(temp_dir, "mentor-skill", "SKILL.md"))
registry <- SkillRegistry$new(temp_dir)
expect_equal(registry$resolve_skill_name("yshu"), "mentor-skill")
expect_equal(registry$resolve_skill_name("Y叔"), "mentor-skill")
expect_equal(registry$find_closest_skill_name("menter-skill"), "mentor-skill")
})
test_that("SkillRegistry finds relevant skills from query and file paths", {
temp_dir <- tempfile()
dir.create(temp_dir)
on.exit(unlink(temp_dir, recursive = TRUE))
dir.create(file.path(temp_dir, "rna-skill"))
writeLines(c(
"---",
"name: rna-skill",
"description: RNA analysis helper",
"when_to_use: Use this when the user asks about differential expression or RNA-seq",
"paths:",
" - counts/*.csv",
"---",
"Body"
), file.path(temp_dir, "rna-skill", "SKILL.md"))
registry <- SkillRegistry$new(temp_dir)
by_query <- registry$find_relevant_skills(query = "我想做 differential expression", limit = 1L)
expect_equal(by_query$name[[1]], "rna-skill")
expect_true(grepl("when_to_use", by_query$matched_by[[1]], fixed = TRUE))
by_path <- registry$find_relevant_skills(
query = "",
file_paths = c("counts/sample.csv"),
cwd = temp_dir,
limit = 1L
)
expect_equal(by_path$name[[1]], "rna-skill")
expect_true(grepl("paths", by_path$matched_by[[1]], fixed = TRUE))
})
test_that("SkillRegistry refresh picks up skill updates on disk", {
temp_dir <- tempfile()
dir.create(temp_dir)
on.exit(unlink(temp_dir, recursive = TRUE))
dir.create(file.path(temp_dir, "live-skill"))
writeLines(c(
"---",
"name: live-skill",
"description: First version",
"---",
"Body v1"
), file.path(temp_dir, "live-skill", "SKILL.md"))
registry <- SkillRegistry$new()
registry$scan_skills(temp_dir, recursive = TRUE)
expect_equal(registry$get_skill("live-skill")$description, "First version")
expect_equal(nrow(registry$list_roots()), 1L)
writeLines(c(
"---",
"name: live-skill",
"description: Updated version",
"---",
"Body v2"
), file.path(temp_dir, "live-skill", "SKILL.md"))
dir.create(file.path(temp_dir, "new-skill"))
writeLines(c(
"---",
"name: new-skill",
"description: Added later",
"---",
"New body"
), file.path(temp_dir, "new-skill", "SKILL.md"))
registry$refresh()
expect_equal(registry$get_skill("live-skill")$description, "Updated version")
expect_true(registry$has_skill("new-skill"))
})
test_that("default_skill_roots includes user install paths and prioritizes project roots", {
old_roots <- getOption("aisdk.skill_roots")
old_env <- Sys.getenv("AISDK_SKILL_PATHS", unset = NA_character_)
on.exit(options(aisdk.skill_roots = old_roots), add = TRUE)
on.exit({
if (is.na(old_env)) Sys.unsetenv("AISDK_SKILL_PATHS") else Sys.setenv(AISDK_SKILL_PATHS = old_env)
}, add = TRUE)
options(aisdk.skill_roots = character(0))
Sys.unsetenv("AISDK_SKILL_PATHS")
project_dir <- tempfile()
dir.create(project_dir)
on.exit(unlink(project_dir, recursive = TRUE), add = TRUE)
project_skills <- file.path(project_dir, ".aisdk", "skills")
dir.create(project_skills, recursive = TRUE)
project_skills <- normalizePath(project_skills, winslash = "/", mustWork = FALSE)
dot_skills <- file.path(project_dir, ".skills")
dir.create(dot_skills, recursive = TRUE)
dot_skills <- normalizePath(dot_skills, winslash = "/", mustWork = FALSE)
roots <- default_skill_roots(project_dir = project_dir)
roots_with_missing <- default_skill_roots(project_dir = project_dir, include_missing = TRUE)
user_agents_roots <- normalizePath(
file.path(Sys.getenv("HOME"), c(".agents", "agents"), "skills"),
winslash = "/",
mustWork = FALSE
)
expect_true(all(user_agents_roots %in% roots_with_missing))
expect_true(dot_skills %in% roots)
expect_true(project_skills %in% roots)
expect_true(any(grepl("\\.skills$", roots)))
expect_true(any(grepl("\\.aisdk/skills$", roots)))
expect_equal(tail(roots, 1), normalizePath(project_skills, winslash = "/", mustWork = FALSE))
})
test_that("auto skill registry discovers project .skills directory", {
project_dir <- tempfile()
skill_dir <- file.path(project_dir, ".skills", "project-skill")
dir.create(skill_dir, recursive = TRUE)
on.exit(unlink(project_dir, recursive = TRUE), add = TRUE)
writeLines(c(
"---",
"name: project-skill",
"description: Project dot skills",
"---",
"Project-local skill body"
), file.path(skill_dir, "SKILL.md"))
registry <- aisdk:::create_auto_skill_registry(project_dir = project_dir, recursive = TRUE)
expect_true(registry$has_skill("project-skill"))
expect_true(any(grepl("\\.skills$", registry$list_roots()$path)))
})
# === Tests for convenience functions ===
test_that("create_skill_registry creates populated registry", {
skip_if_not(dir.exists(fixtures_path), "Test fixtures not found")
registry <- create_skill_registry(fixtures_path)
expect_s3_class(registry, "SkillRegistry")
expect_gte(registry$count(), 1)
})
test_that("scan_skills is alias for create_skill_registry", {
skip_if_not(dir.exists(fixtures_path), "Test fixtures not found")
registry <- scan_skills(fixtures_path)
expect_s3_class(registry, "SkillRegistry")
expect_gte(registry$count(), 1)
})
# === Tests for SkillRegistry$print ===
test_that("SkillRegistry$print outputs summary", {
skip_if_not(dir.exists(fixtures_path), "Test fixtures not found")
registry <- SkillRegistry$new(fixtures_path)
output <- capture.output(print(registry))
expect_true(any(grepl("<SkillRegistry>", output)))
expect_true(any(grepl("registered", output)))
})
# === Tests for SkillRegistry subdirectory scanning ===
test_that("SkillRegistry finds skills in subdirectories (recursive=FALSE)", {
# Create temp structure:
# tmp/
# skill_a/SKILL.md
# SKILL.md (in root)
# nested/deep/SKILL.md (should NOT be found if recursive=FALSE)
temp_dir <- file.path(tempdir(), paste0("test_skills_", as.integer(Sys.time())))
dir.create(temp_dir)
on.exit(unlink(temp_dir, recursive = TRUE))
# Skill in root
writeLines("---
name: root_skill
description: Root skill
---
# Root Skill", file.path(temp_dir, "SKILL.md"))
# Skill in immediate subdir
dir.create(file.path(temp_dir, "subdir_skill"))
writeLines("---
name: subdir_skill
description: Subdir skill
---
# Subdir Skill", file.path(temp_dir, "subdir_skill", "SKILL.md"))
# Skill in nested subdir
dir.create(file.path(temp_dir, "nested", "deep"), recursive = TRUE)
writeLines("---
name: deep_skill
description: Deep skill
---
# Deep Skill", file.path(temp_dir, "nested", "deep", "SKILL.md"))
# Scan with recursive = FALSE
registry <- SkillRegistry$new()
registry$scan_skills(temp_dir, recursive = FALSE)
# Should find root and subdir, but NOT deep
expect_true(registry$has_skill("root_skill"))
expect_true(registry$has_skill("subdir_skill"))
expect_false(registry$has_skill("deep_skill"))
})
test_that("create_skill_registry accepts multiple roots with later roots overriding earlier skills", {
root_a <- tempfile("skills-a-")
root_b <- tempfile("skills-b-")
dir.create(file.path(root_a, "shared"), recursive = TRUE)
dir.create(file.path(root_b, "shared"), recursive = TRUE)
on.exit(unlink(root_a, recursive = TRUE), add = TRUE)
on.exit(unlink(root_b, recursive = TRUE), add = TRUE)
writeLines(c(
"---",
"name: shared",
"description: From A",
"---",
"A"
), file.path(root_a, "shared", "SKILL.md"))
writeLines(c(
"---",
"name: shared",
"description: From B",
"---",
"B"
), file.path(root_b, "shared", "SKILL.md"))
registry <- create_skill_registry(c(root_a, root_b), recursive = TRUE)
expect_equal(registry$get_skill("shared")$description, "From B")
expect_equal(nrow(registry$list_roots()), 2L)
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.