Progressive disclosure of skill resources without eager loading.
Provides APIs to enumerate and lazily load resources bundled with skills:
scripts/- Executable scripts and automationreferences/- Documentation and reference materialsassets/- Binary assets (images, binaries, etc.)
All paths are resolved relative to the skill root directory, preventing path traversal attacks.
Usage
# List all resources in a skill
resources = Jido.AI.Skill.Resources.list_resources(skill_root)
# => %{scripts: [...], references: [...], assets: [...]}
# Load a specific resource
{:ok, content} = Jido.AI.Skill.Resources.load_resource(skill_root, "references/guide.md")
# Check resource exists without loading
true = Jido.AI.Skill.Resources.exists?(skill_root, "scripts/setup.sh")
Summary
Functions
Checks if a resource exists without loading it.
Lists resources of a specific type.
Lists all resources in a skill directory.
Loads a resource file lazily.
Loads a resource as UTF-8 text.
Resolves a relative path to an absolute path within the skill root.
Gets information about a specific resource without loading it.
Searches for resources matching a pattern.
Types
@type resource_info() :: %{ name: String.t(), relative_path: String.t(), absolute_path: String.t(), size: non_neg_integer() | nil, modified: DateTime.t() | nil }
@type resource_listing() :: %{ scripts: [resource_info()], references: [resource_info()], assets: [resource_info()] }
@type resource_type() :: :scripts | :references | :assets
Functions
Checks if a resource exists without loading it.
Examples
true = Jido.AI.Skill.Resources.exists?(skill_root, "scripts/setup.sh")
false = Jido.AI.Skill.Resources.exists?(skill_root, "missing.txt")
@spec list_by_type(String.t(), resource_type()) :: [resource_info()]
Lists resources of a specific type.
Examples
scripts = Jido.AI.Skill.Resources.list_by_type(skill_root, :scripts)
@spec list_resources(String.t()) :: resource_listing()
Lists all resources in a skill directory.
Returns a map with resources grouped by type, without loading content.
Examples
resources = Jido.AI.Skill.Resources.list_resources("/path/to/skill")
IO.inspect(resources.scripts) # [%{name: "deploy.sh", ...}]
IO.inspect(resources.references) # [%{name: "api.md", ...}]
IO.inspect(resources.assets) # [%{name: "logo.png", ...}]
Loads a resource file lazily.
Returns the file content only when requested. Validates the path is within the skill root to prevent directory traversal.
Returns
{:ok, binary}- Resource loaded successfully{:error, :not_found}- Resource doesn't exist{:error, :path_traversal}- Attempted path traversal attack
Examples
{:ok, content} = Jido.AI.Skill.Resources.load_resource(skill_root, "references/api.md")
{:error, :not_found} = Jido.AI.Skill.Resources.load_resource(skill_root, "missing.txt")
# Path traversal is blocked:
{:error, :path_traversal} = Jido.AI.Skill.Resources.load_resource(skill_root, "../../../etc/passwd")
Loads a resource as UTF-8 text.
Same as load_resource/2 but validates the content is valid UTF-8.
Returns
{:ok, String.t()}- Resource loaded and valid UTF-8{:error, :invalid_utf8}- Content is not valid UTF-8{:error, reason}- Other errors fromload_resource/2
Resolves a relative path to an absolute path within the skill root.
Validates the resolved path is within the skill root directory to prevent path traversal attacks.
Returns
{:ok, absolute_path}- Path resolved successfully{:error, :path_traversal}- Path escapes skill root
Examples
{:ok, "/skill/references/guide.md"} = Jido.AI.Skill.Resources.resolve_path("/skill", "references/guide.md")
{:error, :path_traversal} = Jido.AI.Skill.Resources.resolve_path("/skill", "../../../etc/passwd")
@spec resource_info(String.t(), String.t()) :: {:ok, resource_info()} | {:error, :not_found}
Gets information about a specific resource without loading it.
Returns
{:ok, resource_info}- Resource exists, info returned{:error, :not_found}- Resource doesn't exist
Examples
{:ok, info} = Jido.AI.Skill.Resources.resource_info(skill_root, "references/api.md")
IO.puts("Size: #{info.size}")
@spec search(String.t(), String.t()) :: [resource_info()]
Searches for resources matching a pattern.
Examples
# Find all markdown files in references
matches = Jido.AI.Skill.Resources.search(skill_root, "references/**/*.md")