feat(skills): unpack skill packages into standard Agent Skills directories #170

Open
opened 2026-11-12 21:58:26 +00:00 by CoreRasurae · 0 comments
Member

Metadata

  • Commit Message: feat(skills): unpack skill packages into standard Agent Skills directories
  • Branch: feature/m1-skill-directory-unpacking

Background and context

ADR-2034 D-1/D-2 models a skill as one self-contained, content-addressed YAML mapping, and D-6 identifies directory-to-package packing as required authoring tooling. The repository now has a SkillDirectoryPacker API, a pack_skill_directory convenience function, and a utils/pack_skill.py CLI that perform the forward direction (on-disk Agent Skills directory → pkg_skl_ mapping).

The reverse direction is still missing. Inspecting a package, editing it in the familiar Agent Skills layout, republishing it, or handing it to agentskills.io-compatible tooling currently requires manually reconstructing SKILL.md, splitting frontmatter from the body, decoding base64 resources, recreating directories, and guessing the canonical safe_mode representation. There is no API or CLI that deterministically turns a validated skill package back into a standard skill directory.

Current behavior

  • SkillDirectoryPacker / pack_skill_directory only pack a directory into a mapping.
  • utils/pack_skill.py only packs (stdout, --output, --check).
  • There is no mapping-to-directory renderer, no safe resource materializer, and no CLI for unpacking.
  • Users must hand-reconstruct SKILL.md, the allowed-tools frontmatter spelling, UTF-8 vs base64 resource handling, and the canonical safe_mode omission rule.

Expected behavior

  • Add a documented unpacking API (for example SkillDirectoryUnpacker / unpack_skill_package) that accepts either a validated skill package mapping, a Skill value object, or a local YAML package file and writes a standard skill directory.
  • The output directory is named after skill.name and contains a SKILL.md whose YAML frontmatter carries name, description, license, compatibility, metadata, and allowed-tools when present, and whose Markdown body is exactly instructions. The generated frontmatter MUST NOT contain the package-only fields skill, instructions, or resources.
  • safe_mode follows the canonical rule: emit it in SKILL.md only when the package declares safe_mode: false; omit it for the default true.
  • Materialize every resources entry as a file relative to the skill directory, creating parent directories. encoding: utf-8 resources are written verbatim as UTF-8 text; encoding: base64 resources are decoded and written as bytes.
  • Apply the same path-safety rules as packing: reject absolute paths, backslashes, empty/./.. segments, and path or symlink escapes outside the destination.
  • Validate the package (including resource paths and base64 payloads) before writing, and fail without leaving a partial or mixed directory on error.
  • Fail clearly for missing required fields, invalid encodings/base64, invalid resource paths, non-mapping YAML, and a destination that already exists (unless an explicit overwrite option is used).
  • Add an end-user CLI utility in the separate utils/ folder (for example utils/unpack_skill.py) so authors can unpack without writing Python. It should support a local package YAML input, a destination parent directory, and an explicit overwrite/force option.
  • Document both the Python API and the CLI usage with a concrete example in docs/guides/skill-packages.md.

Acceptance criteria

  • Unpacking a package containing references/, assets/, scripts/, a non-standard folder, and root-level files recreates the complete directory tree with the expected relative paths.
  • The generated SKILL.md parses as YAML frontmatter plus Markdown body, uses the allowed-tools spelling, omits skill/instructions/resources from frontmatter, and round-trips its body exactly as instructions.
  • Text resources round-trip byte-for-byte as UTF-8; binary resources round-trip byte-for-byte through base64 decoding.
  • A package without safe_mode produces a SKILL.md without safe_mode; a package with safe_mode: false preserves it; an explicit safe_mode: true is normalized to absence.
  • Packing an unpacked canonical package reproduces the same pkg_skl_ Package ID.
  • An existing destination is rejected unless the overwrite option is explicitly set.
  • Invalid base64, invalid resource paths, missing required fields, and non-mapping YAML fail with clear errors and leave no partial output directory.
  • Behave scenarios cover API round-trip, determinism, binary assets, safe_mode, destination collision, and invalid inputs. A Robot integration scenario unpacks a real package, re-packs it, and loads the result through a local: reference.
  • A CLI scenario proves the utils/ unpack command writes the same layout and reports the Package ID/skill name on standard error.
  • Coverage remains >= 97% via nox -s coverage_report.
  • nox default quality sessions pass.

Supporting information

  • ADR-2034 D-1, D-2, D-3, D-6 and the Package Registry Standard §16 skill schema.
  • Agent Skills specification: https://agentskills.io/specification
  • Modules: cleveractors.agents.skill_packer, cleveractors.agents.skill_schema, cleveractors.registry.canonical, utils/pack_skill.py, docs/guides/skill-packages.md.
  • The existing forward packer and CLI are the reference for canonical frontmatter, resource encoding, and safe_mode emission rules.

Subtasks

  • Implement the unpacking API and destination/path safety rules
  • Render SKILL.md frontmatter/body with canonical field spelling and safe_mode omission
  • Materialize UTF-8 and base64 resources atomically
  • Add a utils/unpack_skill.py CLI with overwrite handling
  • Tests (Behave): round-trip, binary assets, safe_mode, collision, invalid inputs
  • Tests (Robot): unpack → re-pack → load via local:
  • Docs: Python API and CLI usage/example updates
  • Verify coverage >=97% via nox -s coverage_report
  • Run nox (all default sessions), fix any errors

Definition of Done

This issue is complete when:

  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line matches the Commit Message in Metadata exactly.
  • The commit is pushed to the branch matching Branch in Metadata exactly.
  • The commit is submitted as a pull request to develop, reviewed, and merged.
## Metadata - **Commit Message**: `feat(skills): unpack skill packages into standard Agent Skills directories` - **Branch**: `feature/m1-skill-directory-unpacking` ## Background and context ADR-2034 D-1/D-2 models a skill as one self-contained, content-addressed YAML mapping, and D-6 identifies directory-to-package packing as required authoring tooling. The repository now has a `SkillDirectoryPacker` API, a `pack_skill_directory` convenience function, and a `utils/pack_skill.py` CLI that perform the forward direction (on-disk Agent Skills directory → `pkg_skl_` mapping). The reverse direction is still missing. Inspecting a package, editing it in the familiar Agent Skills layout, republishing it, or handing it to agentskills.io-compatible tooling currently requires manually reconstructing `SKILL.md`, splitting frontmatter from the body, decoding base64 resources, recreating directories, and guessing the canonical `safe_mode` representation. There is no API or CLI that deterministically turns a validated skill package back into a standard skill directory. ## Current behavior - `SkillDirectoryPacker` / `pack_skill_directory` only pack a directory into a mapping. - `utils/pack_skill.py` only packs (stdout, `--output`, `--check`). - There is no mapping-to-directory renderer, no safe resource materializer, and no CLI for unpacking. - Users must hand-reconstruct `SKILL.md`, the `allowed-tools` frontmatter spelling, UTF-8 vs base64 resource handling, and the canonical `safe_mode` omission rule. ## Expected behavior - Add a documented unpacking API (for example `SkillDirectoryUnpacker` / `unpack_skill_package`) that accepts either a validated skill package mapping, a `Skill` value object, or a local YAML package file and writes a standard skill directory. - The output directory is named after `skill.name` and contains a `SKILL.md` whose YAML frontmatter carries `name`, `description`, `license`, `compatibility`, `metadata`, and `allowed-tools` when present, and whose Markdown body is exactly `instructions`. The generated frontmatter MUST NOT contain the package-only fields `skill`, `instructions`, or `resources`. - `safe_mode` follows the canonical rule: emit it in `SKILL.md` only when the package declares `safe_mode: false`; omit it for the default `true`. - Materialize every `resources` entry as a file relative to the skill directory, creating parent directories. `encoding: utf-8` resources are written verbatim as UTF-8 text; `encoding: base64` resources are decoded and written as bytes. - Apply the same path-safety rules as packing: reject absolute paths, backslashes, empty/`.`/`..` segments, and path or symlink escapes outside the destination. - Validate the package (including resource paths and base64 payloads) before writing, and fail without leaving a partial or mixed directory on error. - Fail clearly for missing required fields, invalid encodings/base64, invalid resource paths, non-mapping YAML, and a destination that already exists (unless an explicit overwrite option is used). - Add an end-user CLI utility in the separate `utils/` folder (for example `utils/unpack_skill.py`) so authors can unpack without writing Python. It should support a local package YAML input, a destination parent directory, and an explicit overwrite/force option. - Document both the Python API and the CLI usage with a concrete example in `docs/guides/skill-packages.md`. ## Acceptance criteria - Unpacking a package containing `references/`, `assets/`, `scripts/`, a non-standard folder, and root-level files recreates the complete directory tree with the expected relative paths. - The generated `SKILL.md` parses as YAML frontmatter plus Markdown body, uses the `allowed-tools` spelling, omits `skill`/`instructions`/`resources` from frontmatter, and round-trips its body exactly as `instructions`. - Text resources round-trip byte-for-byte as UTF-8; binary resources round-trip byte-for-byte through base64 decoding. - A package without `safe_mode` produces a `SKILL.md` without `safe_mode`; a package with `safe_mode: false` preserves it; an explicit `safe_mode: true` is normalized to absence. - Packing an unpacked canonical package reproduces the same `pkg_skl_` Package ID. - An existing destination is rejected unless the overwrite option is explicitly set. - Invalid base64, invalid resource paths, missing required fields, and non-mapping YAML fail with clear errors and leave no partial output directory. - Behave scenarios cover API round-trip, determinism, binary assets, `safe_mode`, destination collision, and invalid inputs. A Robot integration scenario unpacks a real package, re-packs it, and loads the result through a `local:` reference. - A CLI scenario proves the `utils/` unpack command writes the same layout and reports the Package ID/skill name on standard error. - Coverage remains >= 97% via `nox -s coverage_report`. - `nox` default quality sessions pass. ## Supporting information - ADR-2034 D-1, D-2, D-3, D-6 and the Package Registry Standard §16 skill schema. - Agent Skills specification: https://agentskills.io/specification - Modules: `cleveractors.agents.skill_packer`, `cleveractors.agents.skill_schema`, `cleveractors.registry.canonical`, `utils/pack_skill.py`, `docs/guides/skill-packages.md`. - The existing forward packer and CLI are the reference for canonical frontmatter, resource encoding, and `safe_mode` emission rules. ## Subtasks - [ ] Implement the unpacking API and destination/path safety rules - [ ] Render `SKILL.md` frontmatter/body with canonical field spelling and `safe_mode` omission - [ ] Materialize UTF-8 and base64 resources atomically - [ ] Add a `utils/unpack_skill.py` CLI with overwrite handling - [ ] Tests (Behave): round-trip, binary assets, safe_mode, collision, invalid inputs - [ ] Tests (Robot): unpack → re-pack → load via `local:` - [ ] Docs: Python API and CLI usage/example updates - [ ] Verify coverage >=97% via `nox -s coverage_report` - [ ] Run `nox` (all default sessions), fix any errors ## Definition of Done This issue is complete when: - All subtasks above are completed and checked off. - A Git commit is created where the first line matches the Commit Message in Metadata exactly. - The commit is pushed to the branch matching Branch in Metadata exactly. - The commit is submitted as a pull request to `develop`, reviewed, and merged.
CoreRasurae added this to the v2.1.0 milestone 2026-11-12 21:58:26 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
cleveragents/cleveractors-core#170
No description provided.