Skip to content

fix(list-skills): discover every installed skill, drop the PyYAML import - #1187

Merged
potiuk merged 2 commits into
apache:mainfrom
potiuk:fix-list-skills-discovery
Sep 9, 2026
Merged

fix(list-skills): discover every installed skill, drop the PyYAML import#1187
potiuk merged 2 commits into
apache:mainfrom
potiuk:fix-list-skills-discovery

Conversation

@potiuk

@potiuk potiuk commented Sep 9, 2026

Copy link
Copy Markdown
Member

Summary

  • list-skills resolved its skill set from Path(__file__), so under a per-family marketplace plugin install it listed only the family that ships it — 5 skills out of 74. Discovery is now installation-aware and repository-first.
  • It derived each skill's family from the name prefix, against Golden rule 8 — inventing families like write/ and optimize/ for skills declaring utilities, and splitting repo-health and contributor-growth across headings. Grouping now reads the family: frontmatter key.
  • It was the only script under skills/ importing a third-party module, against the stdlib-only contract in skills/pyproject.toml, and crashed with ModuleNotFoundError: No module named 'yaml' on any interpreter without PyYAML — including every plugin install. Frontmatter is now parsed with the standard library, and PEP 723 inline metadata states the contract.

Discovery

Three sources, unioned and de-duplicated by the name a person would type:

Source Invocation shown
.agents/skills/ + relays (pinned snapshot) /magpie-<skill>
the repo's own skills/ (framework checkout) /magpie-<skill>
sibling plugins in the marketplace cache /<plugin>:<skill>

Relays collapse to one row; a skill reachable from two install methods keeps both, because they are two different things to type. Root comes from git rev-parse --show-toplevel, with --root to override. A skill declaring no family: lands in other.

Type of change

  • Skill change (skills/list-skills/) — eval fixtures updated below
  • CI / dev loop (spec + eval fixtures)

Test plan

  • prek run --all-files passes
  • ruff check / ruff format / mypy / pytest pass for the skills workspace member
  • New skills/list-skills/tests/test_list_skills.py — 17 cases, including the marketplace regression, relay collapse, declared-vs-inferred family, and "a project with its own unrelated skills/ dir is not the framework checkout"
  • Eval suite run for list-skills; 8/8 pass
  • New eval case case-5-marketplace-install pins the one install where the documented command does not apply
  • Measured end to end: 74 in the framework checkout, 10 in an adopter, 47 from a marketplace install against a real plugin cache (was 5)

RFC-AI-0004 compliance

  • Sandbox — read-only; no new host access
  • Vendor neutrality — the agent-target list mirrors the registry in skills/setup/agents.md; no vendor is special-cased

Notes for reviewers

Two judgement calls worth a look:

  1. PEP 723 with dependencies = [], not dependencies = ["pyyaml"]. Declaring PyYAML would have fixed the crash for uv run callers only and left skills/pyproject.toml's stdlib-only guarantee broken for a bare python3. The stdlib parser matches what collect_status.py and skill-and-tool-validator already do.
  2. Step 1's command stays literal. I first rewrote it to <framework>/skills/list-skills/…; models expand that placeholder to an absolute path, which cannot be pinned in a fixture, and cases 1–4 failed. Second commit reverts those four fixtures to their upstream values — they never needed to change.

A sweep found no other script with either bug: nothing else under skills/ derives its working set from __file__ or infers family from a prefix.

Generated-by: Claude Code (Opus 5)

`list-skills` resolved its skill set from `Path(__file__)`, so under a
per-family marketplace plugin install it listed only the family that
happens to ship it — 5 skills out of 74. It also derived each skill's
family from the name prefix, against Golden rule 8, which invented
families like `write/` and `optimize/` for skills declaring `utilities`
and split `repo-health` and `contributor-growth` across headings.

Discovery is now installation-aware and repository-first: the agent
target directories an install writes into, the framework's own `skills/`
when the repository is the framework checkout, and the sibling plugins in
the marketplace cache when the script runs from one. Rows de-duplicate by
the name a person would type, so relays collapse while a skill reachable
from two install methods keeps both. Grouping reads the `family:`
frontmatter key; a skill declaring none lands in `other`.

The script was also the only one under `skills/` importing a third-party
module, against the stdlib-only contract in `skills/pyproject.toml`, and
crashed with `ModuleNotFoundError: No module named 'yaml'` on any
interpreter without PyYAML — including every plugin install. Frontmatter
is now parsed with the standard library, as `collect_status.py` and the
validator already do, and PEP 723 inline metadata states the contract so
`uv run --script` and a bare `python3` behave identically.

Generated-by: Claude Code (Opus 5)
The previous commit rewrote Step 1 to use the `<framework>` placeholder.
Models resolve that to an absolute path, which is machine-specific and
cannot be pinned in an eval fixture, so step-1-command cases 1-4 failed:
expected `<framework>/skills/…`, got `/Users/…/skills/…`.

Step 1 goes back to the literal repository-relative
`.claude/skills/magpie-list-skills/scripts/list_skills.py`, with an
explicit instruction not to expand it, and the four fixtures return to
their upstream values — they never needed to change. The marketplace
exception moves below the primary command and keeps its own case, which
passed throughout.

Generated-by: Claude Code (Opus 5)
@potiuk
potiuk merged commit 826e13d into apache:main Sep 9, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant