Observed since 20 Oct, 17:43 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
abstractskill
Agent Skills (SKILL.md) parsing, discovery, trust gating, and demand derivation for AbstractFramework
AbstractSkill is the shared Python library for Agent Skills (SKILL.md) in the
AbstractFramework ecosystem.
It provides a small, dependency-light foundation for:
SKILL.md frontmatter and instructions<available_skills> prompt blocks for hosts and agentsFlows run; skills are activated. AbstractSkill owns the portable skill contract so abstractruntime,
abstractgateway, and thin clients can share identical semantics without duplicating parsers.
pip install abstractskill
The wheel carries the library and the curated skill registry (the shelf below).
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):
<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);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;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>).
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
)
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 trackinghash_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 readseffective_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 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-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).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.
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.
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.
MIT — see LICENSE.
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
PyYAMLEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | PyYAML>=6.0 | runtime |
pytestEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pytest>=7.0.0; extra == "dev" | runtime |
pytestEnvironment{
"extras": "",
"marker": "extra == \"test\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pytest>=7.0.0; extra == "test" | runtime |
bdist_wheel · 205.3 KB · Python py3
sdist · 209.3 KB · Python source
Observed since 20 Oct, 17:43 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:125ff18478473d0aef0d085fe3a4aee88407dbbbf86902b7fdb02606d99234d1