AbstractSkill
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.mdfrontmatter 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.keptmaps every kept item to its reason; - items an earlier seed wrote that the bundle no longer contains are
reported in
not_in_bundleand 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()andload()resolve identically (a broken copy never shadows a valid one) and degrade loudly (#FALLBACKwarnings via logging and optionalon_warning)content_hash— SHA-256 digest of one document for evolution trackinghash_skill_tree/inspect_skill_dir/read_skill_resource— whole-tree tamper hash (injective manifest), structural inventory (has_scriptsis a structural fact), bounded in-tree resource readseffective_tools/effective_tools_for_skill— grant ∩ allowed-tools composition (skills can narrow below the grant, never widen beyond it; absence ofallowed-toolsimplies nothing)format_available_skills_xml— deterministic discovery prompt blockevaluate_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-closedTrustVerdict(blocked / requires_review / attachable). The curated shelf (first-party + catalog-vendored skills) ships in the package underabstractskill/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:
- Getting started — install, parse, discover, trust, seed, activate
- Architecture — components, data flow and seeding (with diagrams)
- API reference — every public function and type
- FAQ and Troubleshooting
- Trust model and curated skills catalog
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)
| File | Size | Uploaded | |
|---|---|---|---|
| abstractskill-0.3.0.tar.gz | 214.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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