Skip to content

Add a filesystem-backed SkillsSource #62

Description

@shibayan

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    coreUsage: [Issues, PRs], Target: packages/coreskillsUsage: [Issues, PRs], Target: Agent Skills

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions