Skip to content

skills.sh: pin the npx skills add path for the muster-setup skill - #387

Merged
ZacxDev merged 3 commits into
mainfrom
feat/skills-sh
Nov 1, 2026
Merged

ZacxDev merged 3 commits into
mainfrom
feat/skills-sh

Conversation

@ZacxDev

@ZacxDev ZacxDev commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

What

npx skills add ZacxDev/muster installs the same muster-setup skill the Claude Code plugin ships. It already did on main; this PR pins it with tests and makes the README command name the repo and the skill unambiguously. Trimmed after the round 0 review.

Layout decision: no new file

The skills CLI's docs (docs/creating-skills.md, skills 1.7.4):

If .claude-plugin/marketplace.json or .claude-plugin/plugin.json exists, skills declared in those files are also discovered … This enables compatibility with the Claude Code plugin marketplace ecosystem.

Required Fields: name: Unique identifier (lowercase, hyphens allowed); description: Brief explanation of what the skill does

The CLI source (getPluginSkillPaths) adds <plugin source>/skills for every plugin whose source starts with ./. So plugins/muster/skills/muster-setup/SKILL.md is found where it already lives, and both tools read one file.

Measured with the CLI run from its npm tarball, with a throwaway HOME, config dirs and project dir, and telemetry off:

  • --list found exactly muster-setup, both on a local checkout and on ZacxDev/muster.
  • An install wrote a SKILL.md that cmp shows byte-identical to the plugin's file.
  • Negative control: with the file removed, the CLI reports "No skills found".

README

Shows the full command, npx skills add ZacxDev/muster --skill muster-setup, and says "This is muster by ZacxDev". skills find muster returns unrelated skills named muster from other repositories.

Tests (tests/claude_plugin_test.py, already a CI step; 11 tests)

  • Drift guard: the plugin's SKILL.md is the only SKILL.md the repo would publish. It counts committed files plus untracked files that are not ignored (git ls-files --cached --others --exclude-standard). Measured: a copy at skills/muster-setup/SKILL.md is searched first and replaces the plugin's skill for skills users, while Claude Code keeps installing the original.
  • The skill's description stays a plain YAML string. Measured: a ": " in it makes the CLI skip the skill with a YAML parse error, so it finds 0 skills.
  • The README's skills command names a skill the plugin ships.

Red/green: at origin/main only the README test fails. The other two pass on main, so they are invariant guards and not regression tests.

Mutations (each test failed on its own assertion):

  • An untracked, not-ignored copy at skills/muster-setup/SKILL.md failed the drift guard.
  • A committed copy at the same path also failed the drift guard (first round).
  • ": " added to the description failed the description test.

Leak scan: tests/leakscan.py reports 0 findings locally. That run had no key, so the denylist check was skipped; CI runs it with the key.

🤖 Generated with Claude Code

`npx skills add ZacxDev/muster` already installs the plugin's own
plugins/muster/skills/muster-setup/SKILL.md: the skills CLI (1.7.4,
getPluginSkillPaths) searches each marketplace plugin's skills/ directory.
Measured: --list finds exactly muster-setup; an install into a throwaway
project wrote a byte-identical copy. So no second SKILL.md is added.

The README now gives the full command (`--skill muster-setup`) and says
this is muster by ZacxDev: other repositories publish unrelated skills
named `muster`.

tests/claude_plugin_test.py pins, for the skills CLI:
- the plugin's SKILL.md is the only one in the repo (drift guard: a copy
  in skills/ is searched first and, measured, replaces it for skills.sh
  users while Claude Code keeps the original);
- the marketplace entry leads the CLI to the plugin's skills/ directory;
- the frontmatter's name/description stay plain YAML strings (measured:
  a ": " in the description makes the CLI skip the skill, 0 found);
- the README's skills command names a skill the plugin ships.

Red at origin/main: only the README test (the other three are invariant
guards; main already satisfies them). Each new guard was mutated and
failed on its own assertion.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ZacxDev

ZacxDev commented Nov 1, 2026

Copy link
Copy Markdown
Owner Author

Round 0 (requirements & deletion) — verdict: deletion candidates (D1, D2) + premise Q1.
Ledger: round 0 · requirements: 5 (unattributed: 0; UNATTRIBUTED-UNKNOWN: 4) · deletion candidates: 2

D1 — tests/claude_plugin_test.py: delete test_the_cli_searches_the_plugins_skill_directory + skills_cli_plugin_dirs() (re-implements skills-CLI 1.7.4 private search logic; same property already pinned by test_source_is_a_plugin_inside_the_repository + test_the_skill_set)
D2 — tests/claude_plugin_test.py: cut test_frontmatter_parses_as_two_plain_strings to the description check only (name half duplicated by SKILL_NAME regex in test_frontmatter; the "metadata" assertion guards a hazard nothing introduces)
Q1 — The ask's premise (add a SKILL.md so `npx skills add ZacxDev/muster` works) was already satisfied on main; does the operator want this README-sharpening + guard-tests PR merged instead, or closed? · forcing: user

Operator decision on Q1: keep the PR, trimmed. D1 deleted, D2 cut to the description check. Kept: README disambiguation + the only-one-SKILL.md drift guard + the README-names-a-shipped-skill test.

Drop test_the_cli_searches_the_plugins_skill_directory and its helper
skills_cli_plugin_dirs(): it re-implemented the skills CLI's private
search logic, and the existing source/skill-set tests already pin the
layout it depends on.

Cut the YAML frontmatter check to the description only (the name is
pinned by test_frontmatter's regex; the metadata assertion is gone).

The only-one-SKILL.md drift guard already counts untracked files that
are not ignored (git ls-files --others --exclude-standard); an untracked
skills/muster-setup/SKILL.md fails it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ZacxDev
ZacxDev marked this pull request as ready for review November 1, 2026 05:52
@ZacxDev

ZacxDev commented Nov 1, 2026

Copy link
Copy Markdown
Owner Author

Operator's answer to round 0's Q1, given in the coordinating Claude session on 2026-11-01 (the operator picked "Keep, trimmed" when asked; transcribed here by the coordinator):

Q1=answered: keep the PR, trimmed — README disambiguation + the only-one-SKILL.md drift guard + the README-names-a-shipped-skill test; D1 deleted, D2 cut to the description check

@ZacxDev
ZacxDev merged commit a8a2b35 into main Nov 1, 2026
19 checks passed
ZacxDev added a commit that referenced this pull request Nov 2, 2026
…bleWhereAgentsAreListed (#398)

* test(api): preflightStore.NameExists answers from its rows (fix 1-in-256 flake)

TestAPoolWorkerIsVisibleWhereAgentsAreListed failed once in CI (PR #387,
run 41274534531 attempt 1): "/agents/spry-newt carries 1 pool chips, want 0".

Root cause is the fixture, not production: preflightStore.NameExists returned
false unconditionally, so BuildUniqueAgentName never redrew. The name pool is
16 adjectives x 16 nouns = 256, so the test's two dispatches shared a name about
once per 256 runs, and pageablePreflight.GetByName then resolved the pool-less
control's page to the first (pooled) row. PGStore.NameExists consults live
rows, so production redraws; the fixture now does the same.

Matrix (go test ./internal/api -run '^TestAPoolWorkerIsVisibleWhereAgentsAreListed$'):
- natural rate, origin/main, -count=3000: 9 FAIL / 2991 PASS
- natural rate, HEAD,        -count=3000: 0 FAIL / 3000 PASS
- forced collision (generateName pinned to one name, uncommitted),
  old fixture -count=200: 200 FAIL; new fixture -count=200: 0 FAIL
- HEAD, -race -cpu 1,4 -count=500: 1000 PASS / 0 FAIL

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(api): tidy the NameExists fixture comment (audit r1 nit)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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