Motivation
SkillsSource has in-memory, aggregating, filtering, deduplicating and caching pieces, but no way
to load skills from a directory of SKILL.md files — the convention all three reference
implementations support (Python FileSkillsSource, .NET AgentFileSkillsSource, Go fsskills).
Proposed API
Node's fs is required, so this lands on the same Node-only subpath as the other filesystem-backed
pieces:
import { directorySkillsSource } from '@polymind-inc/agent-framework/node';
const source = directorySkillsSource({ paths: ['./skills'] });
Each path may be a skill directory containing SKILL.md, or a parent holding skill
subdirectories. Frontmatter is parsed by the existing parser, which already enforces the same name
and description limits as the reference implementations, and the <available_resources> /
<available_scripts> listing the model sees is already produced by the existing skill types. A
skill that cannot be read is reported through reportSkillError and the rest still load.
Resource scoping is the point of the feature
A read triggered by read_skill_resource must not escape the owning skill's directory:
- Resources and scripts are discovered by walking the skill directory to a bounded depth and are
served by name, so a path chosen by the model never reaches the filesystem directly.
- The resolved path is checked against the skill directory prefix, and any symlink or reparse
point in the path is rejected. Python and .NET both refuse links outright, at discovery time and
again at read time. Resolving links first and then checking the prefix would accept a link that
happens to point inside the directory, which neither reference implementation allows.
- A rejected read reports an error without echoing resolved filesystem paths back to the model.
Acceptance criteria
- Frontmatter parsing and body loading from a directory tree.
../, absolute paths and symlinked resources are all refused, on Windows as well as POSIX.
- One unreadable skill directory does not take the others down with it.
cacheSkills(directorySkillsSource(...)) composes, since discovery runs on every run.
Motivation
SkillsSourcehas in-memory, aggregating, filtering, deduplicating and caching pieces, but no wayto load skills from a directory of
SKILL.mdfiles — the convention all three referenceimplementations support (Python
FileSkillsSource, .NETAgentFileSkillsSource, Gofsskills).Proposed API
Node's
fsis required, so this lands on the same Node-only subpath as the other filesystem-backedpieces:
Each path may be a skill directory containing
SKILL.md, or a parent holding skillsubdirectories. Frontmatter is parsed by the existing parser, which already enforces the same name
and description limits as the reference implementations, and the
<available_resources>/<available_scripts>listing the model sees is already produced by the existing skill types. Askill that cannot be read is reported through
reportSkillErrorand the rest still load.Resource scoping is the point of the feature
A read triggered by
read_skill_resourcemust not escape the owning skill's directory:served by name, so a path chosen by the model never reaches the filesystem directly.
point in the path is rejected. Python and .NET both refuse links outright, at discovery time and
again at read time. Resolving links first and then checking the prefix would accept a link that
happens to point inside the directory, which neither reference implementation allows.
Acceptance criteria
../, absolute paths and symlinked resources are all refused, on Windows as well as POSIX.cacheSkills(directorySkillsSource(...))composes, since discovery runs on every run.