Skip to main content

outskilled

Filesystem-based agent skill loader. One spec, one reference implementation, with a first-party adapter for pydantic-ai.

A skill is a folder containing a SKILL.md file — YAML frontmatter describing what the skill does plus a markdown body explaining how to do it. outskilled discovers skills (arbitrary nesting supported), validates them against the spec, and renders a system-prompt-ready manifest. The pydantic-ai adapter wires the manifest into an Agent's instructions and registers a load_skill tool the model uses to fetch a skill's body on demand (plus opt-in tools for listing skills and reading skill resources). Two lines of glue and the model can route to the right skill.

References

Spec & ecosystem:

Pydantic-AI:

Status

v0.2 — spec stabilising. Adds optional when_to_use and always_load frontmatter fields and a pydantic-ai adapter alongside the framework-agnostic registry. v0.1 skills continue to validate unchanged.

Quickstart

from pathlib import Path
from outskilled import SkillRegistry

# Either pass roots directly...
reg = SkillRegistry([Path("./skills")])

# ...or load them from a config file that lives with your skills:
reg = SkillRegistry.from_config("./skills/skills.yaml")

# Level 1: render the manifest for the system prompt.
print(reg.manifest_xml())

# Level 2: load a skill's body on demand. (The `search` skill ships
# with the example bundle under examples/agent_demo/skills/.)
body = reg.load("search")

SkillRegistry walks every directory under its roots, treats any directory containing SKILL.md as a skill, and validates against the spec. Reserved subdirectories inside a skill (references/, scripts/, assets/) are not descended into. Arbitrary nesting is supported — the path from a skill root to a skill's parent directory is the category path. The same loader handles flat layouts and N-level categories without a nested=True flag.

skills.yaml

A small declarative config (lives inside your skills directory, so the bundle is portable):

# skills/skills.yaml
roots:
  - .                  # paths are resolved relative to this file
  # - ../shared-skills # optional extra roots

SkillRegistry.from_config(path) resolves the listed paths relative to the config file's directory.

Pydantic-AI integration

Install the extra:

pip install "outskilled[pydantic-ai]"

Attach a registry to an existing Agent:

from pydantic_ai import Agent
from outskilled.pydanticai import attach_skills

agent = Agent("anthropic:claude-sonnet-4-6")
attach_skills(agent, "skills/skills.yaml")

Or build an Agent with skills already wired up:

from outskilled.pydanticai import skill_aware_agent

agent = skill_aware_agent(
    "anthropic:claude-sonnet-4-6",
    skills="skills/skills.yaml",
)

What attach_skills adds (each independently togglable):

  • Instructions (Level 1, on by default): an @agent.instructions function that returns the rendered <available_skills> manifest, sorted for deterministic prompt caching.
  • load_skill(name) tool (Level 2, on by default): the model calls this to fetch a skill body on demand. Path-traversal-safe.
  • Inlined always-loaded bodies (on by default): any skill with always_load: true in its frontmatter has its body inlined into the instructions. Independent of the manifest flag — bodies are inlined even when the manifest is suppressed.
  • list_skills tool (opt-in): re-renders the manifest at runtime.
  • read_skill_resource tool (Level 3, opt-in): exposes files under a skill's references/, scripts/, assets/ with path safety.

attach_skills is not idempotent — calling it twice on the same Agent raises SkillError. Build a new Agent if you need a different skill set.

Try it

A self-contained demo lives under examples/agent_demo/ — four skills across three nesting depths, an offline plumbing check, and a real routing eval against Claude Sonnet 4.6.

pip install -e ".[pydantic-ai]"

# Plumbing check (no API key). Verifies the manifest + load_skill
# round-trip, NOT whether an LLM routes correctly.
python examples/agent_demo/run_demo.py

# Real routing eval against an actual model.
export ANTHROPIC_API_KEY=...
python examples/agent_demo/run_live.py

See examples/agent_demo/README.md for the two-script split and what each one does / doesn't prove.

Install

pip install -e .

Python 3.12+. One runtime dep: PyYAML.

Spec

See SPEC.md for the canonical rules: directory layout, frontmatter schema, validation, manifest format, progressive disclosure.

Layout

outskilled/
├── SPEC.md
├── README.md
├── LICENSE
├── pyproject.toml
├── src/outskilled/
│   ├── __init__.py     # public API
│   ├── errors.py       # typed exceptions
│   ├── models.py       # Skill dataclass
│   ├── parser.py       # frontmatter extraction
│   ├── validator.py    # spec §3 rules
│   ├── manifest.py     # XML + markdown renderers
│   ├── registry.py     # discovery + composition
│   └── pydanticai.py   # optional pydantic-ai adapter
├── examples/
│   └── agent_demo/     # end-to-end demo + offline & live runners
├── tests/
└── .github/workflows/
    └── publish.yml     # PyPI publish on Release

Development

pip install -e ".[dev]"
pytest

Release

Publishing is automated via .github/workflows/publish.yml. To cut a release:

  1. Bump version in pyproject.toml and __version__ in src/outskilled/__init__.py (keep them in sync — the workflow checks the tag against pyproject.toml).
  2. Commit, merge to main.
  3. On GitHub, Releases → Draft a new release, create a tag matching the bumped version (e.g. v0.2.1), publish.

The workflow runs the test suite, builds sdist + wheel, and uploads to PyPI via trusted publishing (OIDC — no API token in secrets).

One-time PyPI setup (only needed on the first release): on PyPI, Your projects → outskilled → Publishing → Add a new publisher:

Field Value
Owner phiweger
Repository outskilled
Workflow filename publish.yml
Environment name pypi

For the very first release (before the project exists on PyPI), use the pending publisher flow under Your account → Publishing → Add a pending publisher.

Metadata

Release files for outskilled 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for outskilled 0.1.0
File Size Uploaded Provenance
outskilled-0.1.0.tar.gz 35.9 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for outskilled 0.1.0
File Interpreter ABI Platform
outskilled-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 53.3 kB

Release files / outskilled-0.1.0.tar.gz

Download URL outskilled-0.1.0.tar.gz
Size 35.9 kB
Tags Source
SHA-256 checksum
How to use checksums
12869b10e436f4554d978fa6b63966255316abb5c0a1ea8aeaa298d2d6dc8635
BLAKE2b-256 checksum
How to use checksums
9bba0eb06056c69cf5b56adc3ffb5f29e4711dc64be6e86d276806edd4d30fbb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 20, 2026.

Transparency log

Release files / outskilled-0.1.0-py3-none-any.whl

Download URL outskilled-0.1.0-py3-none-any.whl
Size 17.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a11f4a15840409479ec9a6113b4ff016f574accb8a503b266f6d63794353e658
BLAKE2b-256 checksum
How to use checksums
3a4f4e07df4ce33886971c50986061759301a2cb5b449e43b8e8a8c49229cc11
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 20, 2026.

Transparency log

Security for outskilled 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for outskilled-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page