Filesystem Provider (agentskills-fs)¶
Loads skills from a local directory.
pip install agentskills-fs
agentskills_fs ¶
Local filesystem-based skill provider for the Agent Skills format.
This package provides :class:LocalFileSystemSkillProvider, a concrete
implementation of :class:~agentskills_core.SkillProvider that reads
Agent Skills <https://agentskills.io>_ from a local directory tree.
Install::
pip install agentskills-fs
LocalFileSystemSkillProvider ¶
Bases: SkillProvider
Skill provider backed by a local directory tree.
Each immediate subdirectory of root that contains a SKILL.md
file is treated as a skill. The directory name serves as the skill's
unique identifier and must match the name field in the
SKILL.md YAML frontmatter.
Expected layout::
root/
├── incident-response/
│ ├── SKILL.md # YAML frontmatter + markdown body
│ ├── references/ # optional supplementary docs
│ ├── scripts/ # optional executable code
│ └── assets/ # optional static resources
└── another-skill/
└── SKILL.md
Progressive disclosure guarantees:
- :meth:
get_metadatareads and parses only the YAML frontmatter (between the opening and closing---delimiters). - :meth:
get_bodyreturns only the markdown after the frontmatter. - Resource methods (:meth:
get_reference, :meth:get_script, :meth:get_asset) read individual files on demand. Resource names are discovered by the agent from the skill body.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
root
|
Path
|
Path to the top-level directory containing skill subdirectories. |
required |
max_file_bytes
|
int
|
Maximum allowed file size in bytes.
Files exceeding this limit raise
:class: |
DEFAULT_MAX_FILE_BYTES
|
SKILL.md contents are cached per provider instance after the
first read, because a single skill is otherwise re-read up to five
times in one agent session. Call :meth:invalidate when skills
change on disk.
Resource listing is supported: see :meth:list_resources.
Skill discovery is supported: see :meth:discover.
Raises:
| Type | Description |
|---|---|
NotADirectoryError
|
If root does not exist or is not a directory. |
ValueError
|
If max_file_bytes is negative. |
Example::
provider = LocalFileSystemSkillProvider(Path("./skills"))
registry = SkillRegistry()
await registry.register_all(provider)
skill = registry.get_skill("incident-response")
meta = await skill.get_metadata()
print(f"{meta['name']}: {meta['description']}")
discover
async
¶
discover() -> list[str]
List the skill directories directly beneath the root.
A directory counts as a skill when it holds a SKILL.md.
Dotted directories, loose files and symlinks pointing outside
the root are skipped, matching :meth:list_resources.
Discovery does not read or validate the skills it finds, so a returned ID can still fail registration. That is deliberate: enumerating a folder should not cost one full parse per skill.
Returns:
| Type | Description |
|---|---|
list[str]
|
Sorted skill IDs. Empty when the root holds no skills. |
get_asset
async
¶
get_asset(skill_id: str, name: str) -> bytes
Read a single asset file as raw bytes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
skill_id
|
str
|
Skill name. |
required |
name
|
str
|
Asset filename. |
required |
Returns:
| Type | Description |
|---|---|
bytes
|
Raw file content. |
Raises:
| Type | Description |
|---|---|
ResourceNotFoundError
|
If the file does not exist. |
get_body
async
¶
get_body(skill_id: str) -> str
Return the markdown instruction body after the YAML frontmatter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
skill_id
|
str
|
Skill name to look up. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Markdown text (may be empty if |
Raises:
| Type | Description |
|---|---|
SkillNotFoundError
|
If the skill directory or |
get_metadata
async
¶
get_metadata(skill_id: str) -> dict[str, Any]
Parse and return the YAML frontmatter of a skill's SKILL.md.
Only the content between the opening and closing ---
delimiters is parsed. The markdown body is discarded so that
metadata-only queries remain lightweight.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
skill_id
|
str
|
Skill name to look up. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dictionary of frontmatter key-value pairs. |
Raises:
| Type | Description |
|---|---|
SkillNotFoundError
|
If the skill directory or |
get_reference
async
¶
get_reference(skill_id: str, name: str) -> bytes
Read a single reference file as raw bytes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
skill_id
|
str
|
Skill name. |
required |
name
|
str
|
Reference filename. |
required |
Returns:
| Type | Description |
|---|---|
bytes
|
Raw file content. |
Raises:
| Type | Description |
|---|---|
ResourceNotFoundError
|
If the file does not exist. |
get_script
async
¶
get_script(skill_id: str, name: str) -> bytes
Read a single script file as raw bytes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
skill_id
|
str
|
Skill name. |
required |
name
|
str
|
Script filename. |
required |
Returns:
| Type | Description |
|---|---|
bytes
|
Raw file content. |
Raises:
| Type | Description |
|---|---|
ResourceNotFoundError
|
If the file does not exist. |
invalidate ¶
invalidate(skill_id: str | None = None) -> None
Drop cached SKILL.md content.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
skill_id
|
str | None
|
Skill to forget. Clears the whole cache when omitted. Unknown IDs are ignored. |
None
|
list_files
async
¶
list_files(skill_id: str) -> list[str]
List the complete tree without reading file contents.
Includes SKILL.md, hidden files, and arbitrary nested directories. Links and special files are rejected instead of silently publishing an incomplete manifest. Only publish a directory intended for sharing.
list_resources
async
¶
list_resources(skill_id: str) -> dict[str, list[str]]
List the resource files a skill contains, grouped by kind.
Only regular files directly inside references/, scripts/
and assets/ are reported. Subdirectories, dotfiles and
symlinks pointing outside the skill root are skipped rather than
raising, so one stray entry cannot make a whole skill
unlistable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
skill_id
|
str
|
Skill name. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, list[str]]
|
Mapping of resource kind to sorted filenames. Categories the |
dict[str, list[str]]
|
skill does not use map to an empty list. |
Raises:
| Type | Description |
|---|---|
SkillNotFoundError
|
If the skill directory does not exist. |
read_file
async
¶
read_file(skill_id: str, path: str) -> bytes
Read original bytes from a confined, link-free skill tree.
This deliberately bypasses the parsed SKILL.md cache. No decoding or newline conversion is performed. The configured file-size limit applies.