Skip to main content

AbstractSkill

PyPI version CI Tested Python

AbstractSkill is the shared Python library for Agent Skills (SKILL.md) in the AbstractFramework ecosystem.

It provides a small, dependency-light foundation for:

  • parsing and validating SKILL.md frontmatter and instructions
  • discovering skills on disk (progressive disclosure: metadata first)
  • computing stable content hashes for skill evolution and replay safety
  • formatting compact <available_skills> prompt blocks for hosts and agents
  • composing a skill's tool declarations with an operator grant (never widening)
  • classifying skill trust: validated skills, do-not-use advisories, and a fail-closed verdict (trust model)

Flows run; skills are activated. AbstractSkill owns the portable skill contract so abstractruntime, abstractgateway, and thin clients can share identical semantics without duplicating parsers.

Install

pip install abstractskill

The wheel carries the library and the curated skill registry (the shelf below).

The bundled skill registry

pip install abstractskill installs the reviewed shelf as package data:

abstractskill/registry/skills/            # 14 curated skills (one folder per SKILL.md)
abstractskill/registry/licenses/          # upstream licenses of catalog-vendored skills
abstractskill/registry/validations.yaml   # trust records: byte pins per skill tree
abstractskill/registry/advisories.yaml    # do-not-use advisories (empty at v1, by design)
abstractskill/registry/guidance.yaml      # class-level curation guidance
abstractskill/registry/catalog.yaml       # vendoring catalog + the bundle `version`

In this repository the same files live under src/abstractskill/registry/; docs/skills-catalog.md is the human-readable index.

Hosts do not serve the installed package directory (it is replaced on every upgrade). They copy it into a directory they own with seed_registry:

from pathlib import Path

from abstractskill import bundled_registry_dir, bundled_registry_version, seed_registry

print(bundled_registry_dir(), bundled_registry_version())

report = seed_registry(Path("/srv/my-host/skills-shelf"))
print(report.added, report.updated, report.unchanged)
print(report.kept)          # {item: reason} for everything left untouched
print(report.not_in_bundle) # seeded earlier, no longer bundled

seed_registry is safe to run on every start, and from several processes at once (the whole call holds an exclusive lock on <dest>/.seed.lock):

  • a skill folder or file missing at the destination is added;
  • one byte-identical to the bundle is unchanged;
  • one still byte-identical to what an earlier seed wrote (recorded in <dest>/.seeded.json, compared by tree hash) is replaced by the newer bundled content — unless the installed bundle is older than that seed, in which case it is kept (kept_newer);
  • anything else is kept as it is, with its reason: kept_user_modified (an operator edit), kept_foreign (content no seed wrote), kept_unknown_provenance (the destination has no seed manifest), kept_symlink, kept_unreadable. report.kept maps every kept item to its reason;
  • items an earlier seed wrote that the bundle no longer contains are reported in not_in_bundle and left in place; removing them is the host's decision. Nothing outside the bundle is ever deleted.

Without a manifest (a first seed into a populated folder, or a lost .seeded.json), items byte-identical to the bundle are adopted and recorded; every other existing item is kept as kept_unknown_provenance, because the seed cannot tell an old seeded copy from an operator edit. Keep .seeded.json with the shelf when you back it up or move it.

Seeding twice writes nothing. No network access is involved.

The seeded directory has the layout a host reads as its shelf: <dest>/skills, <dest>/licenses and the four yaml files (validations.yaml, advisories.yaml and guidance.yaml drive the trust gate; catalog.yaml records the curated sources and the bundle version). Trust records bind to content hashes, never to paths, so a seeded shelf verifies wherever it lives, and an edited skill honestly drops to unverified until it is re-validated.

A host uses the API this way: call seed_registry on its own shelf directory at start-up, serve <dest>/skills through select_skills_for_context with the seeded trust files, and surface the report (in particular the kept items and not_in_bundle) to its operators. AbstractGateway, for example, seeds <data dir>/skills/registry when it starts and lets operators point it at another shelf with its skills.shelf setting (console or abstractgateway config set skills.shelf <folder>).

Quick start

from pathlib import Path

from abstractskill import FilesystemSkillLoader, format_available_skills_xml, parse_skill_md

# Parse a SKILL.md file
doc = parse_skill_md(Path("my-skill/SKILL.md").read_text(encoding="utf-8"))
print(doc.metadata.name, doc.metadata.description)

# Discover skills under one or more roots (later roots override earlier ones).
# NOTE: discovery is for LISTING only — it applies no trust gate. Do not pipe
# discover() straight into activation.
loader = FilesystemSkillLoader([Path.home() / ".abstract" / "skills", Path(".abstract/skills")])
skills = loader.discover()
print(format_available_skills_xml(skills))

# Load full instructions when a skill is activated
loaded = loader.load("my-skill")
print(loaded.document.content_hash)

To ACTIVATE skills into a context, gate them through trust in one call so the order (load → hash → evaluate_trust → compose) cannot be skipped:

from pathlib import Path

from abstractskill import TrustRegistry, select_skills_for_context, format_available_skills_xml

shelf = Path("/srv/my-host/skills-shelf")  # a directory filled by seed_registry
registry = TrustRegistry.load(
    validations_path=shelf / "validations.yaml",
    advisories_path=shelf / "advisories.yaml",
)
selection = select_skills_for_context(
    registry, shelf_root=shelf / "skills",
    names=["coredoc", "backlog"],  # names-only is enough: sources derive from the registry
    enabled=[],  # names the operator explicitly review-enabled for this context
)
# Only trust-gated skills reach the prompt; blocked skills never appear.
block = format_available_skills_xml(
    list(selection.active), descriptions=selection.activation_descriptions
)

Package scope

  • parse_skill_md — YAML frontmatter + markdown body (LF/CRLF/CR; spec-validated name/description/compatibility)
  • FilesystemSkillLoader — list metadata and load full documents; discover() and load() resolve identically (a broken copy never shadows a valid one) and degrade loudly (#FALLBACK warnings via logging and optional on_warning)
  • content_hash — SHA-256 digest of one document for evolution tracking
  • hash_skill_tree / inspect_skill_dir / read_skill_resource — whole-tree tamper hash (injective manifest), structural inventory (has_scripts is a structural fact), bounded in-tree resource reads
  • effective_tools / effective_tools_for_skill — grant ∩ allowed-tools composition (skills can narrow below the grant, never widen beyond it; absence of allowed-tools implies nothing)
  • format_available_skills_xml — deterministic discovery prompt block
  • evaluate_trust + TrustRegistry / ValidationRecord / AdvisoryEntry / GuidanceEntry — validated-skill attestations bound to tree hashes, a do-not-use advisory registry (four mandated fields, graded severity), and a fail-closed TrustVerdict (blocked / requires_review / attachable). The curated shelf (first-party + catalog-vendored skills) ships in the package under abstractskill/registry/. See the trust model.
  • select_skills_for_context + SkillSelection — the one trust-gated activation pipeline (load → hash → evaluate → gate); hash-pinned enables; declared MCP/tool dependencies surfaced for host-side refusal.
  • load_catalog / CatalogEntry / SkillCatalog — curated vendoring catalog (pinned upstream commits, expected tree hashes).
  • bundled_registry_dir / bundled_registry_version / seed_registry + SeedReport — the shelf shipped in the wheel and the policy-safe copy into a host directory.
  • derive_demand + DemandReport — derived demand tier from declared tool/MCP requirements joined against a host inventory (informational; hosts enforce grants).

Hashing contract: hash = bytes, parse = meaning

content_hash and hash_skill_tree are byte-exact deliberately — tamper detection must never call two byte-different trees "the same". A CRLF-authored skill and its LF twin parse identically but hash differently: vendor skills from archives or byte-copies, never through EOL-rewriting checkouts (e.g. git autocrlf), or hash verification will honestly report the rewrite as a mismatch.

Out of scope: gateway registry APIs, zip .skill packaging, and runtime activation handlers. Those layers live in abstractgateway and abstractruntime and consume this library.

Documentation

Full documentation is in docs/ and on GitHub Pages:

See also SECURITY.md for the trust guarantees this library does and does not make, CONTRIBUTING.md for the development workflow, and CHANGELOG.md for release history.

Development

python -m pip install -e ".[test]"
python -m pytest -q

See CONTRIBUTING.md for building the package and the docs site, and for the rules that keep the bundled registry verifiable.

License

MIT — see LICENSE.

Release files for abstractskill 0.3.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 abstractskill 0.3.0
File Size Uploaded
abstractskill-0.3.0.tar.gz 214.3 kB Details

Built distribution (wheel)

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

Total release size: 424.6 kB

Release files / abstractskill-0.3.0.tar.gz

Download URL abstractskill-0.3.0.tar.gz
Size 214.3 kB
Tags Source
SHA-256 checksum
How to use checksums
6697532fd10d7ed36c53b1bcab29dd3f890c942a4938d49d44a3ccc689e8e2b0
BLAKE2b-256 checksum
How to use checksums
d807e094641ab9a920812e7e2feaf72cf253c535d517cf28cb0b1066a238b2c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 26, 2026.

Transparency log

Release files / abstractskill-0.3.0-py3-none-any.whl

Download URL abstractskill-0.3.0-py3-none-any.whl
Size 210.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e9f5afd69627f94c5d6431aa50a678192c0c2758083565491efbf5f45daeff06
BLAKE2b-256 checksum
How to use checksums
36399fa3e4a993dbdf0c43d7d0953ab4b2b8a6ce3d9dddab111ab5ef119317cc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 26, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

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