Skip to content

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_metadata reads and parses only the YAML frontmatter (between the opening and closing --- delimiters).
  • :meth:get_body returns 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:~agentskills_core.AgentSkillsError. Defaults to 10 MB.

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 SKILL.md has no body).

Raises:

Type Description
SkillNotFoundError

If the skill directory or SKILL.md does not exist.

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 SKILL.md does not exist.

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.